设计线程安全类

时间:2010-06-27 20:03:58

标签: c# multithreading thread-safety

在阅读MSDN文档时,它总是让您知道类是否是线程安全的。我的问题是你如何设计一个线程安全的类?我不是在谈论用锁定调用类我意味着我正在为Microsoft创建XXX class \ object而我想说它是“线程安全”我需要做什么?

6 个答案:

答案 0 :(得分:8)

使类线程安全的最简单,最简单的方法是使它成为immutable。它的美妙之处在于你再也不用担心锁定了。

配方:在C#中创建所有实例变量readonly(Java中为final)。

  • 一个不可变对象,一旦在构造函数中创建并初始化,就无法更改。
  • 不可变对象是线程安全的。周期。
  • 这与只有常量的类不同。
  • 对于系统的可变部分,您仍需要考虑并处理锁定/同步属性。这是首先编写不可变类的一个原因。

同样见question

答案 1 :(得分:5)

除了这里的其他优秀答案外,还要考虑另一个角度。

如果公共API具有无法以线程安全方式使用的多步操作,则类的内部数据结构是100%线程安全是不够的。

考虑一个已经构建的列表类,无论有多少线程在做什么,无论有多少类型的操作,列表的内部数据结构总是一致的和OK。

考虑以下代码:

if (list.Count > 0)
{
    var item = list[0];
}

这里的问题是在读取Count属性和通过[0]索引器读取第一个元素之间,另一个线程可能已经清除了列表的内容。

创建公共API时,通常会忘记这种类型的线程安全性。在这里,唯一的解决方案是调用代码手动锁定每种此类访问的内容,以防止代码崩溃。

解决此问题的一种方法是列表类型作者考虑典型的使用场景并在类型中添加适当的方法:

public bool TryGetFirstElement(out T element)

然后你会:

T element;
if (list.TryGetFirstElement(out element))
{
    ....

据推测,TryGetFirstElement以线程安全的方式工作,并且永远不会返回true,因为它无法读取第一个元素值。

答案 2 :(得分:4)

要声明该类是线程安全的,您要断言该类中的内部数据结构不会因多个线程的并发访问而被破坏。要进行该断言,您需要在类的代码的关键部分周围引入锁定(在Java中同步),这可能会导致多个并发线程执行它们的损坏。

答案 3 :(得分:0)

非泛型ICollection类提供线程安全性。 IsSynchronizedSyncRoot。遗憾的是,您无法设置IsSynchronized。您可以阅读有关他们的更多信息here

在您的类中,您可以使用IsSynchronized和Syncroot的相似内容,单独公开公共方法/属性,并在方法体内检查它们。您的IsSynchronized将是一个只读属性,因此一旦您的实例初始化,您将无法修改它

bool synchronized = true;
var collection = new MyCustomCollection(synchronized);
var results = collection.DoSomething();

public class MyCustomCollection 
{
  public readonly bool IsSynchronized;
  public MyCustomCollection(bool synchronized)
  {
   IsSynchronized = synchronized
  }

  public ICollection DoSomething()
  { 
    //am wondering if there is a better way to do it without using if/else
    if(IsSynchronized)
    {
     lock(SyncRoot)
     {
       MyPrivateMethodToDoSomething();
     }
    }
    else
    {
      MyPrivateMethodToDoSomething();
    }

  }
}

您可以在Jared Parson's blog

上阅读有关编写线程安全集合的更多信息

答案 4 :(得分:0)

文档并未建议类是线程安全的,只有方法是。为了断言方法是线程安全的,它必须同时从多个线程调用,而不会给出不正确的结果(不正确的结果是返回错误值的方法或对象进入无效状态)。

当文档说

  

任何公共静态(在Visual中共享)   基本)这种类型的成员是线程   安全

这可能意味着类的静态成员不会改变共享状态。

当文档说

  

任何实例成员都不是   保证是线程安全的。

这可能意味着方法具有最小的内部锁定。

当文档说

  

所有公共和受保护的成员   这个类是线程安全的,可能是   从多个同时使用   线程。

这可能意味着您可以调用的所有方法都使用其中的相应锁定。方法也可能不会改变任何共享状态,或者它是一个无锁数据结构,而设计允许并发使用而没有任何锁定。

答案 5 :(得分:0)

线程安全类就是保护类中的数据(实例变量)。最常见的方法是使用 lock 关键字。最常见的新手错误是使用锁定整个类而不是更精细的锁:

lock (this)
{
   //do somethnig
}

问题在于,如果班级做了重要的事情,它会给你带来重大的性能打击。一般规则是尽可能少地锁定

您可以在此处阅读更多内容:lock keyword in C#

当您更深入地了解多线程时,您还可以查看ReaderWriterLoch和Semaphore。但我建议你只从lock关键字开始。