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

blu*_*lds 18 c# multithreading

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

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

Bri*_*eon 25

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

public class Example
{
  // This is the immutable master collection.
  volatile List<string> collection = new List<string>();

  void Writer()
  {
    var copy = new List<string>(collection); // Snapshot the collection.
    copy.Add("hello world"); // Modify the snapshot.
    collection = copy; // Publish the snapshot.
  }

  void Reader()
  {
    List<string> local = collection; // Acquire a local reference for safe reading.
    if (local.Count > 0)
    {
      DoSomething(local[0]);
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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


nem*_*nem 8

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

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

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


wag*_*j45 8

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


Rob*_*los 6

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

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