什么使方法线程安全?规则是什么?

Bob*_*orn 143 c# thread-safety

是否存在使方法线程安全的整体规则/指南?我知道可能有一百万次一次性情况,但一般情况如何呢?这很简单吗?

  1. 如果方法只访问局部变量,那么它是线程安全的.

是吗?这也适用于静态方法吗?

@Cybis提供的一个答案是:

局部变量不能在线程之间共享,因为每个线程都有自己的堆栈.

静态方法也是如此吗?

如果方法传递给引用对象,那是否会破坏线程的安全性?我做了一些研究,并且有很多关于某些情况的研究,但我希望能够通过使用一些规则来定义遵循的指导方针,以确保方法是线程安全的.

所以,我想我的最终问题是:"是否有一个简短的规则列表定义了一个线程安全的方法?如果是这样,它们是什么?"

编辑这里
有很多好处.我认为这个问题的真正答案是:"没有简单的规则来确保线程安全." 凉.精细.但总的来说,我认为接受的答案提供了一个很好的简短摘要.总有例外.就这样吧.我可以忍受这一点.

Tre*_*ley 130

如果方法(实例或静态)仅引用该方法中作用域的变量,那么它是线程安全的,因为每个线程都有自己的堆栈:

在这种情况下,多个线程可以ThreadSafeMethod同时调用而不会出现问题.

public class Thing
{
    public int ThreadSafeMethod(string parameter1)
    {
        int number; // each thread will have its own variable for number.
        number = parameter1.Length;
        return number;
    }
}
Run Code Online (Sandbox Code Playgroud)

如果该方法调用仅引用本地范围变量的其他类方法,则也是如此:

public class Thing
{
    public int ThreadSafeMethod(string parameter1)
    {
        int number;
        number = this.GetLength(parameter1);
        return number;
    }

    private int GetLength(string value)
    {
        int length = value.Length;
        return length;
    }
}
Run Code Online (Sandbox Code Playgroud)

如果方法访问任何(对象状态)属性或字段(实例或静态),则需要使用锁定以确保不会由其他线程修改这些值.

public class Thing
{
    private string someValue; // all threads will read and write to this same field value

    public int NonThreadSafeMethod(string parameter1)
    {
        this.someValue = parameter1;

        int number;

        // Since access to someValue is not synchronised by the class, a separate thread
        // could have changed its value between this thread setting its value at the start 
        // of the method and this line reading its value.
        number = this.someValue.Length;
        return number;
    }
}
Run Code Online (Sandbox Code Playgroud)

您应该知道,传入方法的任何不是结构或不可变的参数都可以被方法范围之外的另一个线程变异.

要确保正确的并发性,您需要使用锁定.

有关详细信息,请参阅lock语句C#referenceReadWriterLockSlim.

锁定对于一次提供一个功能
ReadWriterLockSlim非常有用,如果您需要多个读取器和单个编写器,则非常有用.

  • @Bharadwaj如果有多个线程访问的`Thing`类的一个实例 (24认同)
  • 在第三个例子中,`private string someValue;`不是`static`所以每个实例都会获得该变量的单独副本.那么请你解释一下这不是线程安全的吗? (12认同)

Eri*_*ert 103

如果方法只访问局部变量,那么它是线程安全的.是吗?

绝对不是.您可以编写一个程序,只有一个本地变量从一个线程访问,但该线程不是线程安全的:

/sf/answers/621818221/

这也适用于静态方法吗?

绝对不.

@Cybis提供的一个答案是:"不能在线程之间共享局部变量,因为每个线程都有自己的堆栈."

绝对不.局部变量的区别特征是它只能在本地范围内可见,而不是在临时池上分配.从两个不同的线程访问相同的局部变量是完全合法的.您可以使用匿名方法,lambdas,迭代器块或异步方法来完成此操作.

静态方法也是如此吗?

绝对不.

如果方法传递给引用对象,那是否会破坏线程的安全性?

也许.

我做了一些研究,并且有很多关于某些情况的研究,但我希望能够通过使用一些规则来定义遵循的指导方针,以确保方法是线程安全的.

你将不得不学会以失望的方式生活.这是一个非常困难的主题.

所以,我想我的最终问题是:"是否有一个简短的规则列表定义了一个线程安全的方法?

不.正如您在前面示例中看到的,空方法可以是非线程安全的.您不妨问"是否有一个确保方法正确的规则的简短列表".不,那里没有.线程安全只不过是一种非常复杂的正确性.

此外,您提出问题的事实表明您对线程安全的基本误解.线程安全是程序的全局属性,而不是本地属性.之所以难以做到,是因为您必须完全了解整个程序的线程行为,以确保其安全性.

再看看我的例子:每个方法都是微不足道的.这是方法在"全局"级别相互交互的方式,使程序陷入僵局.您无法查看每种方法并将其视为"安全",然后期望整个程序是安全的,不仅仅是您可以得出结论,因为您的房子是由100%非空心砖制成的房子也是非中空.房屋的空心是整个房屋的整体属性,而不是其各部分属性的集合.

  • 核心声明:_Thread安全是一个全球性的,而不是一个程序的本地属性._ (13认同)
  • @EricLippert:许多类的MSDN文档声明"此类型的公共静态(在Visual Basic中共享)成员是线程安全的.任何实例成员都不保证是线程安全的.或者有时'这种类型是线程安全的'.(例如字符串),当线程安全是全球关注的时候,如何做出这些保证? (5认同)
  • @BobHorn:`class C {public static Func <int> getter; public static Action <int> setter; public static void M(){int x = 0; getter =()=> x; setter = y => {x = y;};}}`调用M(),然后在两个不同的线程上调用C.getter和C.setter.现在可以在两个不同的线程上写入和读取局部变量,即使它是本地的.再说一次:局部变量的定义特征是它是*local*,而不是它在*的堆栈上*. (4认同)
  • @BobHorn:我认为它更多地与理解我们的工具有关,因此我们可以用权威和知识来谈论它们.例如,原始问题背叛了对局部变量的缺乏理解.这个错误很常见,但事实是这是一个错误,应予以纠正.局部变量的定义非常精确,应该相应地对待.:)这不是那些没有得到病理病例的"人"的答案.这些是澄清,以便您可以了解您实际要求的内容.:) (3认同)
  • @mikez:好问题.这些方法都不会改变任何状态,或者当它们这样做时,它们会在没有锁定的情况下安全地改变状态,或者,如果它们使用锁定,那么它们会以保证不违反全局"锁定顺序"的方式这样做.字符串是不可变的数据结构,其方法不会改变任何东西,因此字符串可以很容易地安全. (3认同)
  • @CodeInChaos:好的.但我更愿意不要求人们理解实现细节以正确推理语言.问题是关于C#中的局部变量,所以采用"局部变量"的C#定义似乎是合理的.关于CIL的问题,那么它会得到一个不同的答案; 这是一种具有不同特征的不同语言. (3认同)
  • @BobHorn:好的,如果你不喜欢那个,那么:创建一个具有本地的异步方法。在一个线程上写入本地。执行等待,然后在另一个线程上恢复异步方法。读当地的。现在你有了一个在一个线程上写入并在另一个线程上读取的本地文件。现在,在这种情况下,至少您不会遇到在不同线程上“同时”写入和读取本地数据的问题。我的观点是:“本地”只是使变量“只能通过其名称访问”,并且只能从“方法的文本”中“访问”。这就是“本地”的意思。 (2认同)
  • 埃里克,我读的越多,我越觉得我真的不知道... :-( (2认同)

ear*_*ess 9

没有硬性规定.

以下是使.NET中的代码线程安全的一些规则以及为什么这些不是好的规则:

  1. 它调用的函数和所有函数必须是纯的(没有副作用)并使用局部变量.虽然这会使你的代码在线程安全,但在.NET中这个限制你也可以做很少有趣的事情.
  2. 在公共对象lock上运行的每个函数都必须是常见的.所有锁必须以相同的顺序完成.这将使代码线程安全,但它将非常慢,并且您可能不会使用多个线程.
  3. ...

没有规则可以使代码线程安全,你唯一能做的就是确保你的代码无论多少次被主动执行都会工作,每个线程都可以在任何时候中断,每个线程都在它自己的状态/位置,以及访问公共对象的每个函数(静态或其他).

  • 锁不必慢.锁非常快; 无争议的锁定的数量级为十到一百**纳秒**.有争议的锁当然是任意慢的; 如果因为你正在竞争锁定而放慢速度,那么*重新架构该程序以消除争用*. (8认同)
  • 我想我没能让#2足够清楚.我试图说有一种方法可以使线程安全:在任何常见对象的每次访问周围放置锁.假设存在多个线程,它将产生争用,并且锁将使整个事情变慢.添加锁时,不能盲目地添加锁,它们必须放在非常好的战略位置. (2认同)