And*_*ena 6 .net c# multithreading interlocked thread-safety
我遇到了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.");
Run Code Online (Sandbox Code Playgroud)
而不是传统的lock:
lock(lockObject)
{
// PROCESS SOMETHING HERE
}
Run Code Online (Sandbox Code Playgroud)
问题是:这样做有什么真正的理由吗?它更快还是有一些隐藏的好处?
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;
}
Run Code Online (Sandbox Code Playgroud)
正如@doctorlove建议的那样,这是代码:https://github.com/miensol/SimpleConfigSections/blob/master/SimpleConfigSections/Cache.cs
如果"PROCESS SOMETHING HERE"抛出异常,则CompareExchange示例代码不会释放锁定.
出于这个原因以及更简单,更易读的代码,我更喜欢lock语句.
你可以用try/finally来纠正这个问题,但这会使代码变得更加丑陋.
该链接的ConcurrentDictionary实现有一个错误:它会失败,如果调用者传递一个空键,潜在地使其他线程纺无限期地解除锁定.
至于效率,您的CompareExchange版本本质上是一个Spinlock,如果线程很可能在短时间内被阻塞,它可以很有效.但是插入托管字典可能需要相当长的时间,因为可能需要调整字典的大小.因此,恕我直言,这不是一个好的螺旋锁候选者 - 这可能是浪费,特别是在单处理器系统上.
你的问题没有明确的答案.我会回答:这取决于.
你提供的代码是:
threadId == 0 == no current work)正如您所指出的,您在代码中有一个实际执行"等待"步骤的循环.在您可以访问关键部分之前,不要阻止该线程,而只是刻录CPU.尝试替换你的处理(在你的情况下,调用Remove)Thread.Sleep(2000),你会看到另一个"等待"线程在循环中占用你的所有一个CPU 2s.
这意味着哪一个更好取决于几个因素.例如:有多少并发访问?操作需要多长时间才能完成?你有多少CPU?
我会使用lock而不是Interlocked因为它更容易阅读和维护.例外的情况是,您有一段数百万次的代码,而且您确定的特定用例Interlocked更快.
所以你必须自己衡量两种方法.如果你没有时间,那么你可能不需要担心表演,你应该使用lock.
有点晚了......我已经阅读了你的示例,但简而言之:
最快到最慢的 MT 同步:
和MT一起玩吧!
是的。该类Interlocked提供原子操作,这意味着它们不会像锁一样阻塞其他代码,因为它们实际上并不需要这样做。当您锁定一段代码时,您需要确保没有 2 个线程同时位于其中,这意味着当一个线程位于其中时,所有其他线程都会等待进入,这会占用资源(CPU 时间和空闲线程)。另一方面,原子操作不需要阻止其他原子操作,因为它们是原子的。从概念上讲,它是一个 CPU 操作,下一个操作会在前一个操作之后进入,并且您不会在等待上浪费线程。(顺便说一句,这就是为什么它仅限于非常基本的操作Increment,例如Exchange等)
我认为锁(下面是一个监视器)使用互锁来知道锁是否已经被占用,但它不知道它内部的操作可以是原子的。
但在大多数情况下,差异并不重要。但您需要针对您的具体情况进行验证。