设计线程安全类

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

使类线程安全的最简单和最简单的方法是使其不可变 。 它的美妙之处在于你再也不用担心锁定了。

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

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

也看到这个问题 。

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

如果公共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 ,因为它无法读取第一个元素值。

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

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

当文件说

此类型的任何公共静态(在Visual Basic中为Shared)成员都是线程安全的。

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

当文件说

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

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

当文件说

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

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

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

 lock (this) { //do somethnig } 

问题在于,如果class级做了重要的事情,它可以给你一个重大的性能打击。 一般规则是尽可能少锁定尽可能短的时间

你可以在这里阅读更多: c#中的lock关键字

当你更深入地理解multithreading时,你也可以看一下ReaderWriterLoch和Semaphore。 但我建议你只从lock关键字开始。

非通用ICollection类提供线程安全性。 IsSynchronized和SyncRoot 。 遗憾的是,您无法设置IsSynchronized。 你可以在这里阅读更多相关信息

在您的类中,您可以使用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的博客上阅读有关编写线程安全集合的更多信息