许多读者,一位作家 – 是否有可能避免锁定?

假设你有一个内存中的字符串列表,以及一个multithreading系统,有许多读者但只有一个编写器线程。

一般来说,是否可以在C#中实现这种系统而不使用锁? 实现是否会对线程如何交互做出任何假设(或者对它们可以做什么进行限制,何时)?

是。 诀窍是确保列表保持不变。 编写器将对主集合进行快照,修改快照,然后将快照发布到包含对主集合的引用的变量。 以下示例演示了这一点。

 public class Example { // This is the immutable master collection. volatile List collection = new List(); void Writer() { var copy = new List(collection); // Snapshot the collection. copy.Add("hello world"); // Modify the snapshot. collection = copy; // Publish the snapshot. } void Reader() { List local = collection; // Acquire a local reference for safe reading. if (local.Count > 0) { DoSomething(local[0]); } } } 

这种方法有几点需要注意。

  • 它只能起作用,因为只有一个作家。
  • 写入是O(n)操作。
  • 不同的读者可能同时使用不同版本的列表。
  • 这是一个相当危险的伎俩。 使用volatile原因非常具体,为什么在读者端获取本地引用等等。如果您不理解这些原因,请不要使用该模式。 有太多可能出错的地方。
  • 这是线程安全的概念是语义的。 不,它不会在时空中抛出exception,爆炸或撕裂整体。 但是,这种模式还有其他方式可能导致问题。 知道有什么限制。 对于每种情况来说,这都不是奇迹般的治疗方法。

由于上述限制,这将使您受益的情况非常有限。 最大的问题是写入首先要求完整拷贝,因此它们可能很慢。 但是,如果写入很少,那么这可能是可以忍受的。

我在这里的答案中描述了更多的模式,包括对多个作者来说安全的模式。

为避免锁定,您可能需要考虑Microsoft的并发集合 。 这些集合提供了对有序和无序表单中对象集合的线程安全访问。 他们使用一些巧妙的技巧来避免在尽可能多的实例内部锁定 。

对于线程库来说,这是一个相当普遍的请求 – 这种锁通常被称为“读写器锁”,或者是该主题的一些变体。 我没有必要专门使用C#实现,但有一个: http : //msdn.microsoft.com/en-us/library/system.threading.readerwriterlockslim.aspx

当然,你遇到的问题是,如果读者总是在阅读,你将永远无法让作家写作。 我相信你必须自己处理。

(好吧,所以它在技术上仍然是一个“锁定”,但它不是C#“锁定”构造,它是一个专门针对问题中所述目的而设计的更复杂的对象。所以我猜它是否是一个正确的答案在某种程度上取决于语义和他为什么问这个问题。)

您还可以使用Microsoft的新Immutable Collections库: http : //blogs.msdn.com/b/bclteam/archive/2012/12/18/preview-of-immutable-collections-released-on-nuget.aspx

注意:这与Concurrent Collections完全分开。

如果编写器仅在头部或尾部插入/删除,则可以使用单链接列表方法而无需锁定。 在任何一种情况下,如果事先构造新节点,则只需要一个primefaces操作(head = newHead;或tail.next = newTail)即可使读取操作可见。

在性能方面,插入和删除是O(1),而长度计算是O(n)。