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# 试图用SpinWait.spinUtil()替换锁(),但它不起作用_C#_Multithreading_Concurrency_Thread Safety_Spinwait - Fatal编程技术网

C# 试图用SpinWait.spinUtil()替换锁(),但它不起作用

C# 试图用SpinWait.spinUtil()替换锁(),但它不起作用,c#,multithreading,concurrency,thread-safety,spinwait,C#,Multithreading,Concurrency,Thread Safety,Spinwait,让我们从代码开始 checkedUnlock是一个HashSet \u hashsetLock是一个对象 lock (_hashsetLock) newMap = checkedUnlock.Add(uniqueId); vs fun在int中 SpinWait.SpinUntil(() => Interlocked.CompareExchange(ref fun, 1, 0) == 1); newMap = checkedUnlock.Add(uniqueId); fun =

让我们从代码开始

checkedUnlock
是一个
HashSet

\u hashsetLock
是一个对象

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

fun
在int中

SpinWait.SpinUntil(() => Interlocked.CompareExchange(ref fun, 1, 0) == 1);
newMap = checkedUnlock.Add(uniqueId);
fun = 0;
我的理解是,这个场景中的
SpinWait
应该像
lock()
一样工作,但是
HashSet
中添加了更多的项,有时它与lock匹配,有时又多了1到5个项,这表明它显然不起作用

我的理解有缺陷吗

编辑

我尝试了这个,它似乎起了作用,我的测试显示到目前为止与
lock()
相同的数字

SpinWait spin = new SpinWait();
while (Interlocked.CompareExchange(ref fun, 1, 0) == 1)
   spin.SpinOnce();
那么,为什么它可以使用这个而不能使用
SpinWait.spinUtil()

编辑#2

完整的小应用程序来查看

在这段代码中,
SpinWait.spinUtil
有时会爆炸(add将引发异常),但当它工作时,计数将不同,因此我对这一个的预期行为是错误的

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();
        }

    }
}
使用系统;
使用System.Collections.Generic;
使用系统线程;
使用System.Threading.Tasks;
命名空间控制台应用程序1
{
班级计划
{
静态void Main(字符串[]参数)
{
var list=新列表();
var rnd=新的随机变量(42);
对于(变量i=0;i<1000000;++i)
列表。添加(rnd.Next(500000));
对象_lock1=新对象();
var hashset1=新HashSet();
int_lock2=0;
var hashset2=新的HashSet();
int_lock3=0;
var hashset3=新的HashSet();
Parallel.ForEach(列表,项=>
{
/******************/
锁(锁1)
hashset1.添加(项);
/******************/
/******************/
SpinWait.SpinUntil(()=>Interlocked.CompareExchange(参考锁2,1,0)==1);
hashset2.添加(项);
_lock2=0;
/******************/
/******************/
SpinWait spin=新的SpinWait();
while(联锁。比较交换(参考锁定3,1,0)==1)
spin.SpinOnce();
hashset3.添加(项);
_lock3=0;
/******************/
});
WriteLine(“Lock:{0}”,hashset1.Count);
WriteLine(“SpinWaitUntil:{0}”,hashset2.Count);
WriteLine(“SpinWait:{0}”,hashset3.Count);
Console.ReadKey();
}
}
}

SpinWait.SpinUtil中使用的条件错误

  • 返回变量的原始值
  • MSDN的文件说,情况是
  • 反复执行直到返回true的委托

    您希望旋转直到发生0->1转换,因此条件应为

    Interlocked.CompareExchange(ref fun, 1, 0) == 0
    
    在其他线程上调用CompareExchange的后续结果为1,因此它们将等待“winner”线程将
    fun
    标志恢复为0

    还有一些评论:

    • fun=0
      应该适用于x86体系结构,但我不确定它在任何地方都是正确的。如果使用Interlocked访问某个字段,那么最好对该字段的所有访问使用Interlocked。因此,我建议使用
      联锁的.Exchange(ref-fun,0)
    • 就性能而言,SpinWait很少是一个好的解决方案,因为它可以防止操作系统将正在旋转的线程置于空闲状态。它只能用于非常短的等待。(). 简单锁(又名监视器,输入/退出)或SimaPraceSLIM一般都会做,或者如果读写的>*,可以考虑Read RealSerkLoSLIM。

    为什么不使用数据类型之类的并发哈希集,请参见?这些都是为并发访问而优化的,它们甚至不需要锁。
    锁有什么问题?你看过
    信号量lim
    类了吗?这是一个很好的选择,而且更容易推理。@ckuri在我的代码中,这是一个只添加的场景,并发字典是为读取而优化的,我没有使用的好处it@tigerswithguitars从代码上看,它使用了spinwait、lock和Monitor的混合,有点重,有点清晰,如果您能更详细地了解一下为什么
    不适用,以及
    信号量lim
    太重的原因,那将很有帮助。似乎用例很重要,这是一个有趣的问题。我使用CompareExchange的方式是模拟锁的正确方式,其他联机示例只需执行一个!=原始值,我改为一个==新值,如果我使用==0,它会/会在几秒钟内爆炸,因为它就像没有锁一样,直接进行交换也不会达到我预期的效果,如果有另一个线程处理代码的这一部分,它就会停止。对于spinwait的使用,可以清楚地确定它应该在很短的时间内使用,添加到hashset的操作应该属于该类别CompareExchange返回原始值、更改前的值(如果有)。CompareExchange返回0表示标志为0,并且在当前调用期间已更改为1。在任何其他情况下,CompareExchange返回1,这意味着“锁被占用”。@Fredou Try。我希望这能让你信服。这个代码示例不做真正的多线程,只需在围绕整个代码的任务中放置一个循环,一个任务将运行X个时间量,然后第二个任务将启动X个时间量,它不会在线程之间切换。但是你的权利,对于spinduntil,compareExchange与SpintWait逻辑的实例相反,这导致了我的困惑