许多读者,一个作家 - 是否可以避免锁定
本文关键字:作家 是否 可以避免 锁定 一个 许多读 | 更新日期: 2023-09-27 18:33:33
假设你有一个内存中的字符串列表和一个多线程系统,有许多读取器,但只有一个编写器线程。
通常,是否可以在不使用锁的情况下在 C# 中实现这种系统?实现是否会对线程如何交互做出任何假设(或限制它们可以做什么,何时执行(?
是的。诀窍是确保列表保持不可变。编写器将创建主集合的快照,修改快照,然后将快照发布到保存对主集合的引用的变量。下面的示例对此进行了演示。
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]);
}
}
}
这种方法有几个注意事项。
- 它之所以有效,是因为只有一个作家。
- 写入是 O(n( 操作。
- 不同的读者可能同时使用不同版本的列表。
- 这是一个相当危险的伎俩。使用
volatile
有非常具体的原因,为什么在阅读器端获取本地参考等。如果您不了解这些原因,请不要使用该模式。有太多可能出错的地方。 - 这是线程安全的概念是语义上的。不,它不会抛出异常,炸毁或撕裂时空中的整体。但是,这种模式还有其他方式会导致问题。了解限制是什么。这不是针对每种情况的灵丹妙药。
由于上述限制,这将使您受益的情况非常有限。最大的问题是写入首先需要完整副本,因此它们可能会很慢。但是,如果写入不频繁,那么这可能是可以容忍的。
我在这里的回答中也描述了更多模式,包括一种对多个作者安全的模式。
线程库来说,这是一个相当常见的请求 - 这种锁通常只称为"读写器锁",或该主题的一些变体。我从来没有专门使用 C# 实现,但有一个:http://msdn.microsoft.com/en-us/library/system.threading.readerwriterlockslim.aspx
当然,你会遇到一个问题,如果读者总是在阅读,你永远无法让作家写作。我相信,你必须自己处理。
(好的,所以它在技术上仍然是一个"锁",但它不是 C# "锁"构造,它是一个更复杂的对象,专门为问题中所述的目的而设计。所以我想这是否是一个正确的答案在某种程度上取决于语义和他为什么问这个问题。
若要避免锁定,可能需要考虑Microsoft的并发集合。这些集合提供对有序和无序形式的对象集合的线程安全访问。他们使用一些巧妙的技巧来避免在尽可能多的情况下在内部锁定。
使用Microsoft新的不可变集合库:http://blogs.msdn.com/b/bclteam/archive/2012/12/18/preview-of-immutable-collections-released-on-nuget.aspx
注意:这与并发集合完全分开。
链列表方法可以在没有锁的情况下使用,前提是编写器只在头部或尾部插入/删除。无论哪种情况,如果事先构造新节点,则只需要一个原子操作(head = newHead;或tail.next = newTail(即可使该操作对读者可见。
在性能方面,插入和删除为 O(1(,而长度计算为 O(n(。