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

时间:2012-03-24 00:28:03

标签: c# thread-safety

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

  1. 如果方法只访问局部变量,则它是线程安全的。
  2. 是吗?这也适用于静态方法吗?

    @Cybis提供的一个答案是:

      

    无法在线程之间共享局部变量,因为每个线程都有自己的堆栈。

    静态方法也是如此吗?

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

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

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

4 个答案:

答案 0 :(得分:128)

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

在这种情况下,多个线程可以同时调用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;
    }
}

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

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;
    }
}

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

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;
    }
}

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

为了确保正确的并发性,您需要使用锁定。

有关详细信息,请参阅lock statement C# referenceReadWriterLockSlim

lock 主要用于提供一次一个功能,
如果您需要多个读者和单个作家,ReadWriterLockSlim非常有用。

答案 1 :(得分:101)

  

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

绝对不是。你可以编写一个只有一个局部变量的程序,这个局部变量是从单个线程访问的,但仍然不是线程安全的:

https://stackoverflow.com/a/8883117/88656

  

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

绝对不是。

  

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

绝对不是。局部变量的显着特征是仅在本地范围中可见,而不是在临时池上分配 从两个不同的线程访问相同的局部变量是完全合法的,也是可能的。您可以使用匿名方法,lambdas,迭代器块或异步方法来实现。

  

静态方法也是如此吗?

绝对不是。

  

如果方法传递了一个引用对象,那会破坏线程的安全性吗?

也许。

  

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

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

  

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

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

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

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

答案 2 :(得分:9)

没有严格的规则。

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

  1. 它调用的函数和所有函数必须是纯的(没有副作用)并使用局部变量。虽然这会使你的代码在线程安全,但在.NET中这个限制你也可以做很多有趣的事情。
  2. 对共同对象进行操作的每个函数都必须lock。所有锁必须以相同的顺序完成。这将使代码线程安全,但它将非常慢,并且您可能不会使用多个线程。
  3. ...
  4. 没有规则使代码线程安全,你唯一能做的就是确保你的代码无论多少次被主动执行都会工作,每个线程都可以在任何时候中断,每个线程处于其自己的状态/位置,这适用于访问公共对象的每个函数(静态或其他)。

答案 3 :(得分:4)

必须使用对象锁定,无状态或不可变来同步它。

link:http://docs.oracle.com/javase/tutorial/essential/concurrency/immutable.html