繁体   English   中英

过度使用Interlocked.exchange?

[英]Overuse of Interlocked.exchange?

我试图了解Interlocked.Exchange的正确用法,所以我正在实现具有添加和删除功能的简单排序的LinkedList。

如果这不是线程安全列表,显然是要找到插入点,您将具有以下类似内容,以找到要插入的正确点,然后再插入新节点。

    public void Insert(int newValue)
    {
        var prev = _header;
        Node curr = _header.Next;

        while(curr != null && curr.value > newValue )
        {
            prev = curr;
            curr = curr.Next;
        }
        var newNode = new Node(newValue, curr);
        prev.Next = newNode;
    }

以下是我对如何为并发列表执行此操作的看法。 Interlocked.Exchange正在进行太多吗? 没有这个,插入是否仍然是线程安全的? 数百或数千个互锁操作会导致性能下降吗?

    public void InsertAsync(int newValue)
    {
        var prev = _header;
        Node curr = new Node(0, null);
        Interlocked.Exchange(ref curr, _header.Next);

        while (curr != null && curr.value > newValue)
        {
            prev = Interlocked.Exchange(ref curr, curr.Next);
        }
        //need some locking around prev.next first, ensure not modified/deleted, etc..
        //not in the scope of this question.
        var newNode = new Node(newValue, prev.Next);
        prev.Next = newNode;
    }

我知道,例如,curr = curr.next是原子读取,但是我可以确定特定线程在没有互锁的情况下将读取curr.next的最新值吗?

使用Interlocked方法有两件事:

  1. 它执行通常不是原子的一系列操作,并使它们有效地成为原子。 对于Exchange您可以执行以下操作: var temp = first; first=second; return temp; var temp = first; first=second; return temp; 但这样做时,没有任何风险被另一个线程修改的风险。
  2. 它引入了内存障碍。 编译器,运行时和/或硬件优化可能会导致不同的线程具有本地“副本”值,该值在技术上位于共享内存中(通常是由于缓存变量)。 这可能导致一个线程花费很长时间才能“看到”另一线程中的写入结果。 内存屏障实际上同步了同一变量的所有这些不同版本。

因此,专门针对您的代码。 您的第二个解决方案实际上不是线程安全的。 每个单独的Interlocked操作都是原子的,但是对各种Interlocked调用的任意数量的调用都不是原子的。 考虑到方法所执行的所有操作,实际上关键部分要大得多。 您将需要使用lock或其他类似的机制(即信号量或监视器)来将对代码段的访问限制为仅一个线程。 在您的特殊情况下,我想整个方法是关键部分。 如果您真的很谨慎,也许可以有几个较小的关键区,但是要确保没有可能出现的竞赛条件将非常困难。

至于性能,因为代码不起作用,因此性能无关紧要。

实现无锁的链表比这要复杂得多。 我强烈建议您不要这样做。 只需使用普通的旧显示器即可。 如果只是出于兴趣,那么您需要对无锁算法进行一些研究。

为了更直接地回答您的问题,互锁操作的成本取决于变量的争用量。 在无竞争的情况下(例如单线程),成本极低。 随着并发访问次数的增加,成本也随之增加。 原因是互锁操作并非真正无锁。 它只是使用硬件锁而不是软件锁。 因此,无锁算法的性能通常不如您所期望的那样好。

您只是在保护分配,但是无论如何它们都是原子的。 您应该检查Exchanged是否真的成功。

但这仍然不能使列表成为线程安全的,并发线程可能会在插入点之前插入一个比newValue大的新值。 我认为该列表甚至可能最终被打破。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM