-1

让我们从代码开始;

checkedUnlock是一个HashSet<ulong>

_hashsetLock是一个对象

lock (_hashsetLock)
    newMap = checkedUnlock.Add(uniqueId);

对比

fun在一个整数

SpinWait.SpinUntil(() => Interlocked.CompareExchange(ref fun, 1, 0) == 1);
newMap = checkedUnlock.Add(uniqueId);
fun = 0;

我的理解是,SpinWait在这种情况下应该像 一样工作,lock()但是添加了更多的项目HashSet,有时它匹配锁,有时其中还有 1 到 5 个项目,这很明显它不起作用

我的理解有缺陷吗?

编辑

我试过了,它似乎有效,我的测试显示与目前相同的lock()数字

SpinWait spin = new SpinWait();
while (Interlocked.CompareExchange(ref fun, 1, 0) == 1)
   spin.SpinOnce();

那么为什么它会与这个一起工作但不是 SpinWait.SpinUntil()呢?

编辑#2

小全应用看

在这段代码中,SpinWait.SpinUntil有时会爆炸(添加会抛出异常)但是当它工作时,计数会不同,所以我对此的预期行为是错误的

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            var list = new List<int>();
            var rnd = new Random(42);
            for (var i = 0; i < 1000000; ++i)
                list.Add(rnd.Next(500000));



            object _lock1 = new object();
            var hashset1 = new HashSet<int>();

            int _lock2 = 0;
            var hashset2 = new HashSet<int>();

            int _lock3 = 0;
            var hashset3 = new HashSet<int>();

            Parallel.ForEach(list, item =>

            {
                /******************/
                lock (_lock1)
                    hashset1.Add(item);
                /******************/

                /******************/
                SpinWait.SpinUntil(() => Interlocked.CompareExchange(ref _lock2, 1, 0) == 1);

                hashset2.Add(item);

                _lock2 = 0;
                /******************/

                /******************/
                SpinWait spin = new SpinWait();
                while (Interlocked.CompareExchange(ref _lock3, 1, 0) == 1)
                    spin.SpinOnce();

                hashset3.Add(item);

                _lock3 = 0;
                /******************/

            });


            Console.WriteLine("Lock: {0}", hashset1.Count);
            Console.WriteLine("SpinWaitUntil: {0}", hashset2.Count);
            Console.WriteLine("SpinWait: {0}", hashset3.Count);

            Console.ReadKey();
        }

    }
}
4

1 回答 1

-1

SpinWait.SpinUntil 中使用的条件是错误的。

  1. Interlocked.CompareExchange返回变量的原始值。
  2. SpinWait.SpinUntil的 MSDN 文档说,条件是

一个要反复执行的委托,直到它返回 true。

您想旋转直到发生 0 -> 1 转换,所以条件应该是

Interlocked.CompareExchange(ref fun, 1, 0) == 0

在其他线程上对 CompareExchange 的后续调用会导致 1,因此它们将一直等待,直到fun“获胜者”线程将标志恢复为 0。

一些进一步的说明:

  • fun = 0;应该适用于 x86 架构,但我不确定它是否在任何地方都是正确的。如果您使用 Interlocked 访问某个字段,最好使用 Interlocked 访问该字段的所有访问权限。所以我建议Interlocked.Exchange(ref fun, 0)改为。
  • SpinWait 在性能方面很少是一个好的解决方案,因为它可以防止操作系统将旋转线程置于空闲状态。它应该只用于非常短的等待。(一个正确使用的例子)。简单的锁(又名 Monitor.Enter/Exit)或 SemaphoreSlim 通常可以使用,或者如果读取次数 >> 写入次数,您可以考虑 ReaderWriterLockSlim。
于 2018-08-08T17:34:17.340 回答