Interlocked.CompareExchange真的比简单的锁更快吗?

 Lovely_Janle 发布于 2023-02-07 19:59

我遇到了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

2 个回答
  • 你的问题没有明确的答案.我会回答:这取决于.

    你提供的代码是:

      等待对象处于已知状态(threadId == 0 == no current work)

      做工作

      将已知状态设置回对象

      另一个线程现在也可以工作,因为它可以从第1步到第2步

    正如您所指出的,您在代码中有一个实际执行"等待"步骤的循环.在您可以访问关键部分之前,不要阻止该线程,而只是刻录CPU.尝试替换你的处理(在你的情况下,调用Remove)Thread.Sleep(2000),你会看到另一个"等待"线程在循环中占用你的所有一个CPU 2s.

    这意味着哪一个更好取决于几个因素.例如:有多少并发访问?操作需要多长时间才能完成?你有多少CPU?

    我会使用lock而不是Interlocked因为它更容易阅读和维护.例外的情况是,您有一段数百万次的代码,而且您确定的特定用例Interlocked更快.

    所以你必须自己衡量两种方法.如果你没有时间,那么你可能不需要担心表演,你应该使用lock.

    2023-02-07 20:01 回答
  • 如果"PROCESS SOMETHING HERE"抛出异常,则CompareExchange示例代码不会释放锁定.

    出于这个原因以及更简单,更易读的代码,我更喜欢lock语句.

    你可以用try/finally来纠正这个问题,但这会使代码变得更加丑陋.

    该链接的ConcurrentDictionary实现有一个错误:它会失败,如果调用者传递一个空键,潜在地使其他线程纺无限期地解除锁定.

    至于效率,您的CompareExchange版本本质上是一个Spinlock,如果线程很可能在短时间内被阻塞,它可以很有效.但是插入托管字典可能需要相当长的时间,因为可能需要调整字典的大小.因此,恕我直言,这不是一个好的螺旋锁候选者 - 这可能是浪费,特别是在单处理器系统上.

    2023-02-07 20:05 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有