提问人:user22155685 提问时间:10/31/2023 更新时间:10/31/2023 访问量:41
Interlocked.MemoryBarrierProcessWide() 如何在内部工作?
How does Interlocked.MemoryBarrierProcessWide() work internally?
问:
基于这篇文章: https://www.albahari.com/threading/part4.aspx 我们知道,易失性不会阻止先写后读被交换,这可能会产生脑筋急转弯:
class IfYouThinkYouUnderstandVolatile
{
volatile int x, y;
void Test1() // Executed on one thread
{
x = 1; // Volatile write (release-fence)
int a = y; // Volatile read (acquire-fence)
...
}
void Test2() // Executed on another thread
{
y = 1; // Volatile write (release-fence)
int b = x; // Volatile read (acquire-fence)
...
}
}
A 和 B 都有可能以 0 的值结束(尽管在 X 和 Y 上都使用了 volatile):
基于本文: https://onurgumus.github.io/2021/02/07/Demystifying-the-volatile-keyword.html
我们可以将示例修复为
解决方案一:
class IfYouThinkYouUnderstandVolatile
{
volatile int x, y;
void Test1() // Executed on one thread
{
x = 1;
Interlocked.MemoryBarrier();
int a = y;
...
}
void Test2() // Executed on another thread
{
y = 1;
Interlocked.MemoryBarrier();
int b = x;
...
}
}
解决方案二:
class IfYouThinkYouUnderstandVolatile
{
volatile int x, y;
void Test1() // Executed on one thread
{
x = 1;
Interlocked.MemoryBarrierProcessWide();
int a = y;
...
}
void Test2() // Executed on another thread
{
y = 1;
int b = x;
...
}
}
我对解决方案二有疑问。我可以理解可能存在第三种方法。使用相同变量的此类代码可能隐藏在不同的库中,我们可能不知道它。这就是作者使用Interlocked.MemoryBarrierProcessWide();
但是,由于仅在一个地方使用,编译器如何知道它需要在 Test2 方法的指令中插入另一个内存屏障,甚至是访问这些字段的另一个方法?Interlocked.MemoryBarrierProcessWide();
答:
1赞
Matthew Watson
10/31/2023
#1
该实现仅调用 Windows API 函数 FlushProcessWriteBuffers():
该函数生成处理器间中断 (IPI) 到所有 属于当前进程相关性的处理器。
因此,要回答您的问题“由于 Interlocked.MemoryBarrierProcessWide();只在一个地方使用,编译器怎么知道它需要在 Test2 方法的指令中插入另一个内存屏障“:
编译器不需要这样做,因为调用 将影响运行任何代码的所有处理器。FlushProcessWriteBuffers()
评论
0赞
user22155685
11/1/2023
对不起,仍然感到困惑,如果一个线程首先执行 Test2() 方法,所以它可能会先执行(编译器可以在 Test2() 中对指令重新排序)然后 b 为 0,然后发生上下文切换,另一个线程正常执行 Test2() 方法,所以也让 y = 0,如何防止这种情况?int b = x;
int a = y;
FlushProcessWriteBuffers()
评论