.Net CompareExchange重新排序
本文关键字:排序 新排序 CompareExchange Net | 更新日期: 2023-09-27 18:19:25
编译器或处理器能否对以下指令重新排序,以便另一个线程看到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);
否。使用Interlock
将表示内存已满。"也就是说,在对Interlocked
方法的调用之前写入的任何变量都在Interlocked
方法之前执行,而在调用之后读取的任何变量在调用之后执行。"[1]他们使用volatile读/写方法来防止b = 1
在a = 1
之前执行。
[1] :Jeffrey Richter:"CLR via C#-Third Edition"第五部分线程,第803页
当然可以。组成CompareExchange
操作的各个操作不能明显地重新排序,但对CompareExchange
的两个调用可以从另一个线程的角度重新排序,只要执行此代码的线程不能观察到这种行为。
CompareExchange
的同步工具正在防止操作之间的可观察到的重新排序影响与该操作相关的内存位置,而不是任何一般的操作,该代码中也没有任何内容可以阻止编译器或JITter完全重新排序这两个CompareExchange
调用(从另一个线程的角度来看)。
你读的理论太多了。是的,如果另一个线程进行,在实践中可能会发生这种情况
Console.WriteLine("a: {0}, b: {1}", a, b);
由于用于格式化字符串的String.Format具有签名
String Format(string fmt, params object[] args)
{
....
}
由于装箱,您的整数将被复制。唯一需要为true的条件是,线程时间片在复制了处于单元化状态的时结束。稍后,当线程恢复工作时,两个变量都设置为一,但控制台的输出将是
a: 0, b: 1
如果你在没有意识到的情况下使用值的副本,那么看到其他值的意义就会立即发生。这就是为什么你通常让其他人编写正确的无锁代码的原因。如果你试着用Console.WriteLine调试无锁代码,我祝你好运。
尽管a和b是按顺序设置的(我不认为JIT编译器会重新排序您的互锁调用),但您不能保证其他线程只能看到两个零或两个一。你可以试着先读取b,然后检查它是否有值1,然后你就可以推导出a的值,但在这种情况下,你甚至不需要a的值。至少对于x86内存模型来说应该是这样。ARM等较弱的内存模型可以打破这种假设。