Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 过度使用联锁交换?_C#_Multithreading_.net 4.0_Lockless - Fatal编程技术网

C# 过度使用联锁交换?

C# 过度使用联锁交换?,c#,multithreading,.net-4.0,lockless,C#,Multithreading,.net 4.0,Lockless,我试图理解Interlocked.Exchange的正确用法,因此我实现了一个带有添加和删除功能的简单排序LinkedList 如果这不是一个线程安全列表,显然要找到插入点,您需要像下面这样找到正确的插入点,然后再找到新节点 public void Insert(int newValue) { var prev = _header; Node curr = _header.Next; while(curr != null &

我试图理解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;
    }
下面是我对如何为并发列表执行此操作的看法。是否有太多的互锁。交换正在进行?如果没有这一点,插件仍然是螺纹安全的吗?成百上千的联锁操作会导致性能下降吗

    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的最新值?

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


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

您只是在保护分配,但它们是原子的。您应该检查交换是否实际成功

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

使用联锁方法可以做两件事:

它执行一些通常不是原子的一系列操作,并使它们有效地原子化。在交换的情况下,您执行的等效操作为:var temp=first;第一=第二;返回温度;但这样做时,两个变量都不会被另一个线程修改。 它引入了一个内存屏障。编译器、运行时和/或硬件优化可能会导致不同的线程拥有一个值的本地副本,该值在技术上是共享内存中的,这通常是缓存变量的结果。这可能导致一个线程需要很长时间才能看到另一个线程中写入的结果。内存屏障本质上是同步同一变量的所有这些不同版本。 因此,请特别注意您的代码。您的第二个解决方案实际上不是线程安全的。每个单独的联锁操作都是原子的,但对各种联锁调用的任意数量的调用都不是原子的。考虑到你所做的一切,你的关键部分实际上要大得多;您需要使用lock或其他类似机制,即信号量或监视器,将对代码段的访问限制为仅单个线程。在您的特殊情况下,我认为整个方法是一个关键部分。如果你真的非常小心,你可能会有几个较小的关键块,但这将是非常困难的,以确保没有可能的比赛条件作为结果

至于性能,因为代码不起作用,所以性能是不相关的。

使用联锁方法做两件事:

它执行一些通常不是原子的一系列操作,并使它们有效地原子化。在交换的情况下,您执行的等效操作为:var temp=first;第一=第二;返回温度;但这样做时,两个变量都不会被另一个线程修改。 它引入了一个内存屏障。编译器、运行时和/或硬件优化可能会导致不同的线程拥有一个值的本地副本,该值在技术上是共享内存中的,这通常是缓存变量的结果。这可能导致一个线程需要很长时间才能看到另一个线程中写入的结果。内存屏障本质上是同步同一变量的所有这些不同版本。 因此,请特别注意您的代码。您的第二个解决方案实际上不是线程安全的。每个单独的联锁操作都是原子的,但对各种联锁调用的任意数量的调用都不是原子的。考虑到你所做的一切,你的关键部分实际上要大得多;您需要使用lock或其他类似机制,即信号量或监视器,将对代码段的访问限制为仅单个线程。在您的特殊情况下,我认为整个方法是一个关键部分。如果你真的非常小心,你可能会有几个较小的关键块,但这将是非常困难的,以确保没有可能的比赛条件作为结果


至于性能,因为代码不起作用,所以性能与此无关。

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

更多
直接回答您的问题,联锁操作的成本取决于对变量的争用量。在无争用的场景中,例如单线程,成本非常低。随着并发访问数量的增加,成本也随之增加。原因是联锁操作并不是真正的无锁操作。它只是使用硬件锁而不是软件锁。由于这个原因,无锁算法的性能往往不如您直观地期望的那样好。

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


为了更直接地回答您的问题,联锁操作的成本取决于对变量的争用量。在无争用的场景中,例如单线程,成本非常低。随着并发访问数量的增加,成本也随之增加。原因是联锁操作并不是真正的无锁操作。它只是使用硬件锁而不是软件锁。由于这个原因,无锁算法的性能往往不如您直观地期望的那样好。

我知道分配是原子的。但我要做的是确保curr.next是最新的。我知道任务是原子的。但我试图做到的是确保curr.next是最新的。为什么我可以保证curr.next没有被其他线程修改?curr是一个局部变量,指向链表中的某个节点。该节点的下一个线程可以被任何线程修改。为什么我可以保证current.Next没有被其他线程修改?除非你真的阻止任何人访问它,否则你不能这样做。如果您只是想要一个线程安全的链表,正如我所说的,那么您可能只需要将所有方法包装在lock语句中。做任何更复杂的事情都是非常困难的,而且你不可能做得正确。好的,明白了。只是为了确保我在这里没有100%的偏离基准-理论上,“curr=curr.next”和“Interlocked.Exchangeref curr,curr.next”可能会导致对curr的不同赋值,对吗?@user981225不,这是不正确的。实际上,您只是在执行一个直接赋值,这已经是原子赋值了。但是为什么curr.next的值不能过时/缓存呢?Curr是一个局部变量,指向链表中的某个节点。该节点的“下一个”可以由另一个线程修改。编辑:我不是想和你们争论,只是想理解:为什么我可以确信当前的。下一个没有被另一个线程修改?curr是一个局部变量,指向链表中的某个节点。该节点的下一个线程可以被任何线程修改。为什么我可以保证current.Next没有被其他线程修改?除非你真的阻止任何人访问它,否则你不能这样做。如果您只是想要一个线程安全的链表,正如我所说的,那么您可能只需要将所有方法包装在lock语句中。做任何更复杂的事情都是非常困难的,而且你不可能做得正确。好的,明白了。只是为了确保我在这里没有100%的偏离基准-理论上,“curr=curr.next”和“Interlocked.Exchangeref curr,curr.next”可能会导致对curr的不同赋值,对吗?@user981225不,这是不正确的。实际上,您只是在执行一个直接赋值,这已经是原子赋值了。但是为什么curr.next的值不能过时/缓存呢?Curr是一个局部变量,指向链表中的某个节点。该节点的“下一个”可以由另一个线程修改。编辑:我不是想和你争论,只是想理解: