我遇到了ConcurrentDictionary
.NET 3.5 的实现(我很抱歉,我现在可以找到链接),它使用这种方法进行锁定:
var current = Thread.CurrentThread.ManagedThreadId; while (Interlocked.CompareExchange(ref owner, current, 0) != current) { } // PROCESS SOMETHING HERE if (current != Interlocked.Exchange(ref owner, 0)) throw new UnauthorizedAccessException("Thread had access to cache even though it shouldn't have.");
而不是传统的lock
:
lock(lockObject) { // PROCESS SOMETHING HERE }
问题是:这样做有什么真正的理由吗?它更快还是有一些隐藏的好处?
PS:我知道有ConcurrentDictionary
一些最新版本的.NET,但我不能用于遗留项目.
编辑:
在我的具体情况下,我正在做的只是Dictionary
以一种线程安全的方式操作内部类.
例:
public bool RemoveItem(TKey key) { // open lock var current = Thread.CurrentThread.ManagedThreadId; while (Interlocked.CompareExchange(ref owner, current, 0) != current) { } // real processing starts here (entries is a regular `Dictionary` class. var found = entries.Remove(key); // verify lock if (current != Interlocked.Exchange(ref owner, 0)) throw new UnauthorizedAccessException("Thread had access to cache even though it shouldn't have."); return found; }
正如@doctorlove建议的那样,这是代码:https://github.com/miensol/SimpleConfigSections/blob/master/SimpleConfigSections/Cache.cs
你的问题没有明确的答案.我会回答:这取决于.
你提供的代码是:
等待对象处于已知状态(threadId == 0 == no current work
)
做工作
将已知状态设置回对象
另一个线程现在也可以工作,因为它可以从第1步到第2步
正如您所指出的,您在代码中有一个实际执行"等待"步骤的循环.在您可以访问关键部分之前,不要阻止该线程,而只是刻录CPU.尝试替换你的处理(在你的情况下,调用Remove
)Thread.Sleep(2000)
,你会看到另一个"等待"线程在循环中占用你的所有一个CPU 2s.
这意味着哪一个更好取决于几个因素.例如:有多少并发访问?操作需要多长时间才能完成?你有多少CPU?
我会使用lock
而不是Interlocked
因为它更容易阅读和维护.例外的情况是,您有一段数百万次的代码,而且您确定的特定用例Interlocked
更快.
所以你必须自己衡量两种方法.如果你没有时间,那么你可能不需要担心表演,你应该使用lock
.
如果"PROCESS SOMETHING HERE"抛出异常,则CompareExchange示例代码不会释放锁定.
出于这个原因以及更简单,更易读的代码,我更喜欢lock语句.
你可以用try/finally来纠正这个问题,但这会使代码变得更加丑陋.
该链接的ConcurrentDictionary
实现有一个错误:它会失败,如果调用者传递一个空键,潜在地使其他线程纺无限期地解除锁定.
至于效率,您的CompareExchange版本本质上是一个Spinlock,如果线程很可能在短时间内被阻塞,它可以很有效.但是插入托管字典可能需要相当长的时间,因为可能需要调整字典的大小.因此,恕我直言,这不是一个好的螺旋锁候选者 - 这可能是浪费,特别是在单处理器系统上.