我怎么知道这个 C# 方法是否是线程安全的?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/420895/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 02:45:46  来源:igfitidea点击:

How do I know if this C# method is thread safe?

c#concurrencystatic-methods

提问by MatthewMartin

I'm working on creating a call back function for an ASP.NET cache item removal event.

我正在为 ASP.NET 缓存项删除事件创建回调函数。

The documentation says I should call a method on an object or calls I know will exist (will be in scope), such as a static method, but it said I need to ensure the static is thread safe.

文档说我应该在一个对象上调用一个方法或者调用我知道将存在(将在范围内),例如静态方法,但它说我需要确保静态是线程安全的。

Part 1: What are some examples of things I could do to make it un-thread safe?

第 1 部分:我可以做哪些事情来使其非线程安全?

Part 2: Does this mean that if I have

第 2 部分:这是否意味着如果我有

static int addOne(int someNumber){
    int foo = someNumber;
    return foo +1; 
}

and I call Class.addOne(5); and Class.addOne(6); simutaneously, Might I get 6 or 7 returned depending on who which invocation sets foo first? (i.e. a race condition)

我打电话给 Class.addOne(5); 和 Class.addOne(6); 同时,根据哪个调用首先设置 foo,我是否会得到 6 或 7 的返回值?(即竞争条件)

采纳答案by Cybis

That addOnefunction is indeed thread safe because it doesn't access any data that could be accessed by another thread. Local variables cannot be shared among threads because each thread gets its own stack. You do have to make sure, however, that the function parameters are value types and not reference types.

addOne功能确实是线程安全的,因为它不访问可能被其他线程访问的任何数据。局部变量不能在线程之间共享,因为每个线程都有自己的堆栈。但是,您必须确保函数参数是值类型而不是引用类型。

static void MyFunction(int x) { ... } // thread safe. The int is copied onto the local stack.

static void MyFunction(Object o) { ... } // Not thread safe. Since o is a reference type, it might be shared among multiple threads. 

回答by Jon Skeet

No, addOne is thread-safe here - it only uses local variables. Here's an example which wouldn'tbe thread-safe:

不,addOne 在这里是线程安全的——它只使用局部变量。这是一个不是线程安全的示例:

 class BadCounter
 {
       private static int counter;

       public static int Increment()
       {
             int temp = counter;
             temp++;
             counter = temp;
             return counter;
       }
 }

Here, two threads could both call Increment at the same time, and end up only incrementing once. (Using return ++counter;would be just as bad, by the way - the above is a more explicit version of the same thing. I expanded it so it would be more obviously wrong.)

在这里,两个线程可以同时调用 Increment,并且最终只增加一次。(return ++counter;顺便说一句,使用也一样糟糕——上面是同一件事的更明确的版本。我扩展了它,所以它会更明显是错误的。)

The details of what is and isn't thread-safe can be quite tricky, but in general if you're not mutating any state (other than what was passed into you, anyway - bit of a grey area there) then it's usuallyokay.

什么是线程安全和什么不是线程安全的细节可能非常棘手,但总的来说,如果您没有改变任何状态(除了传递给您的内容,无论如何 - 那里有一点灰色区域),那么通常没问题.

回答by Oskar

any access to an obect that can be used simultaneously by two threads is not threadsafe.

对可以被两个线程同时使用的对象的任何访问都不是线程安全的。

your example in Part 2 is clearly safe, as it uses only values passed in as arguments, but if you used an object scoped variable you might have to surround the access with appropriate lock statements

您在第 2 部分中的示例显然是安全的,因为它仅使用作为参数传入的值,但是如果您使用对象范围的变量,则可能必须用适当的锁定语句包围访问

回答by yfeldblum

foois not shared between concurrent or sequential invocations, so addOneis thread-safe.

foo在并发或顺序调用之间不共享,因此addOne是线程安全的。

回答by TheSmurf

This would only be a race condition if it were modifying some variable external to the function. Your example is not doing that.

如果它正在修改函数外部的某些变量,这只会是竞争条件。你的例子没有这样做。

That's basically what you're looking out for. Thread safe means that the function either:

这基本上就是你要寻找的。线程安全意味着该函数要么:

  1. Does not modify external data, or
  2. Access to external data is properly synchronized so that only one function can access it at any one time.
  1. 不修改外部数据,或
  2. 对外部数据的访问是正确同步的,因此在任何时候只有一个功能可以访问它。

External data could be something held in storage (database/file), or something internal to the application (a variable, an instance of a class, etc): basically anything that is declared anywhere in the world that is outside of the function's scope.

外部数据可以是保存在存储中的东西(数据库/文件),也可以是应用程序内部的东西(变量、类的实例等):基本上是在函数作用域之外的世界任何地方声明的任何东西。

A trivial example of an un-thread safe version of your function would be this:

函数的非线程安全版本的一个简单示例是:

private int myVar = 0;

private void addOne(int someNumber)
{
   myVar += someNumber;
}

If you call this from two different threads without synchronization, querying the value of myVar will be different depending on whether the query happens after all calls to addOne are complete, or the query happens in between the two calls, or the query happens before either of the calls.

如果你在两个不同的线程中不同步地调用这个,则查询 myVar 的值会有所不同,这取决于查询是在对 addOne 的所有调用完成之后发生,还是发生在两次调用之间,或者查询发生在任何一个调用之前电话。

回答by JoshBerke

Your method is fine since it is only using local variables, let's change your method a bit:

你的方法很好,因为它只使用局部变量,让我们稍微改变一下你的方法:

static int foo;

static int addOne(int someNumber)
{
  foo=someNumber; 
  return foo++;
}

This is not a thread safe method because we are touching static data. This would then need to be modified to be:

这不是线程安全的方法,因为我们正在接触静态数据。这将需要修改为:

static int foo;
static object addOneLocker=new object();
static int addOne(int someNumber)
{
  int myCalc;
  lock(addOneLocker)
  {
     foo=someNumber; 
     myCalc= foo++;
  }
  return myCalc;
}

Which I think this is a silly sample I just did cause if I'm reading it correctly there is no point in foo anymore but hey it's a sample.

我认为这是一个愚蠢的样本,我只是因为如果我正确阅读它,那么 foo 就没有意义了,但嘿,这是一个样本。

回答by Quibblesome

In the above example no.

在上面的例子中没有。

Thread safety is mainly to do with stored state. You can make the above example non thread safe by doing this:

线程安全主要与存储状态有关。您可以通过执行以下操作使上述示例非线程安全:

static int myInt;

static int addOne(int someNumber){
myInt = someNumber;
return myInt +1; 
}

This will mean that due to context switching thread 1 might get to the call myInt = someNumber and then context switch, lets say thread 1 just set it to 5. Then imagine that thread 2 comes in and uses 6 and returns 7. Then when thread 1 wakes up again it will have 6 in myInt instead of the 5 that it was using and return 7 instead of the expected 6. :O

这意味着由于上下文切换,线程 1 可能会调用 myInt = someNumber 然后进行上下文切换,假设线程 1 只是将其设置为 5。然后想象线程 2 进入并使用 6 并返回 7。然后当线程1 再次醒来,它将在 myInt 中有 6 而不是它正在使用的 5 并返回 7 而不是预期的 6。:O

回答by quamrana

The reason that 'foo' and 'someNumber' are safe in your example is that they reside on the stack, and each thread has their own stack and so are not shared.

'foo' 和 'someNumber' 在您的示例中是安全的原因是它们驻留在堆栈上,并且每个线程都有自己的堆栈,因此不共享。

As soon as data has the potential to be shared, for example, being global or sharing pointers to objects, then you could have conflicts and may need to use locks of some sort.

一旦数据有可能被共享,例如,成为全局的或共享指向对象的指针,那么您可能会发生冲突并且可能需要使用某种锁。

回答by Thomas Danecker

There is some research going on which allows you to detect non-thread-safe code. E.g. the project CHESS at Microsoft Research.

正在进行一些研究,允许您检测非线程安全代码。例如微软研究院的 CHESS项目。

回答by daniel

Anywhere, thread safemeans that you don't have two or more threads colliding when you are accessing a resource. Usually static varaiables --- in languages like C#, VB.NET and Java --- made your code thread unsafe.

在任何地方,线程安全意味着您在访问资源时不会有两个或更多线程发生冲突。通常静态变量——在 C#、VB.NET 和 Java 等语言中——使你的代码线程不安全

In Java exists the synchronizedkeyword. But in .NET you get the assembly option/directive:

在Java 中存在synchronized关键字。但是在 .NET 中,您可以获得程序集选项/指令:


class Foo
{
    [MethodImpl(MethodImplOptions.Synchronized)]
    public void Bar(object obj)
    {
        // do something...
    }
}

Examples of non thread safe classes should be singletons, depending on how is this pattern coded. Usually it must implement a synchronizedinstance creator.

非线程安全类的示例应该是单例,具体取决于此模式的编码方式。通常它必须实现一个同步的实例创建者。

If you don't want a synchronized method, you can try locking method, such as spin-lock.

如果你不想要一个同步方法,你可以尝试锁定方法,比如spin-lock