编译器或处理器可以重新排序以下指令,以便另一个线程看到a == 0
和b == 1
吗?
假设某处int a = 0, b = 0;
。
System.Threading.Interlocked.CompareExchange<int>(ref a, 1, 0);
System.Threading.Interlocked.CompareExchange<int>(ref b, 1, 0);
答案 0 :(得分:5)
没有。使用Interlock
将发出完整的内存栅栏信号。 “也就是说,在调用Interlocked
方法之前执行的任何变量在Interlocked
方法之前执行,并且在调用之后调用执行后读取任何变量。”[1]他们使用volatile读/写在b = 1
之前阻止a = 1
的方法。
[1]:Jeffrey Richter:“CLR通过C# - 第三版”第V部分线程,第803页
答案 1 :(得分:3)
当然可以。组成CompareExchange
操作的各个操作无法被可观察地重新排序,但是对CompareExchange
的两次调用可以从另一个线程的角度重新排序,只要执行此代码的线程无法观察到这样的情况。行为。
适用于CompareExchange
的同步工具阻止了影响与该操作相关的内存位置的操作之间的可观察重新排序,而不是一般的任何操作,也没有该代码中的任何内容防止编译器或JITter完全重新排序这两个CompareExchange
调用(从另一个线程的角度来看)。
答案 2 :(得分:3)
你正在读太多理论。是的,如果其他线程
,它可能会在实践中发生Console.WriteLine("a: {0}, b: {1}", a, b);
由于用于格式化字符串的String.Format具有
的签名 String Format(string fmt, params object[] args)
{
....
}
你的整数会因拳击而被复制。唯一需要满足的条件是线程时间片在复制了其单位化状态时结束。稍后当线程恢复工作时,两个变量都设置为1,但控制台输出将是
a: 0, b: 1
如果您在没有意识到的情况下处理值的副本,则会立即看到其他值。这就是为什么你通常让其他人写正确的无锁代码的原因。如果您尝试使用Console.WriteLine调试无锁代码,我希望您好运。
虽然a和b按顺序设置(我不认为JIT编译器会重新排序你的互锁调用)但你不能保证其他线程只能看到两个零或两个零。您可以尝试先读取b并检查它是否具有值1,然后您可以推导出a的值,但在这种情况下,您甚至不需要a的值。至少对于x86内存模型应该如此。这种假设可以通过ARM等较弱的内存模型来打破。