gpt4 book ai didi

c# - 为什么我们需要 Thread.MemoryBarrier()?

转载 作者:IT王子 更新时间:2023-10-29 03:51:46 29 4
gpt4 key购买 nike

在“C# 4 in a Nutshell”中,作者展示了这个类有时可以在没有 MemoryBarrier 的情况下写入 0,尽管我无法在我的 Core2Duo 中重现:

public class Foo
{
int _answer;
bool _complete;
public void A()
{
_answer = 123;
//Thread.MemoryBarrier(); // Barrier 1
_complete = true;
//Thread.MemoryBarrier(); // Barrier 2
}
public void B()
{
//Thread.MemoryBarrier(); // Barrier 3
if (_complete)
{
//Thread.MemoryBarrier(); // Barrier 4
Console.WriteLine(_answer);
}
}
}

private static void ThreadInverteOrdemComandos()
{
Foo obj = new Foo();

Task.Factory.StartNew(obj.A);
Task.Factory.StartNew(obj.B);

Thread.Sleep(10);
}

这种需求对我来说似乎很疯狂。我如何识别所有可能发生这种情况的情况?我认为如果处理器改变操作顺序,它需要保证行为不会改变。

您是否费心使用 Barriers?

最佳答案

您将很难重现此错误。事实上,我什至会说您永远无法使用 .NET Framework 重现它。原因是因为微软的实现使用了强大的内存模型进行写入。这意味着写入被视为易变的。 volatile 写入具有锁定释放语义,这意味着所有先前的写入必须在当前写入之前提交。

但是,ECMA 规范的内存模型较弱。因此,从理论上讲,Mono 甚至 .NET Framework 的 future 版本可能会开始出现错误行为。

所以我要说的是,移除障碍 #1 和 #2 不太可能对程序的行为产生任何影响。当然,这不是保证,而是仅基于 CLR 当前实现的观察结果。

移除障碍 #3 和 #4 肯定会产生影响。这实际上很容易重现。好吧,不是这个例子本身,但下面的代码是更知名的演示之一。它必须使用发布版本进行编译并在调试器之外运行。错误是程序没有结束。您可以通过在 while 循环内调用 Thread.MemoryBarrier 或将 stop 标记为 volatile 来修复该错误>.

class Program
{
static bool stop = false;

public static void Main(string[] args)
{
var t = new Thread(() =>
{
Console.WriteLine("thread begin");
bool toggle = false;
while (!stop)
{
toggle = !toggle;
}
Console.WriteLine("thread end");
});
t.Start();
Thread.Sleep(1000);
stop = true;
Console.WriteLine("stop = true");
Console.WriteLine("waiting...");
t.Join();
}
}

某些线程错误难以重现的原因是因为您用来模拟线程交错的相同策略实际上可以修复错误。 Thread.Sleep 是最著名的示例,因为它会生成内存屏障。您可以通过在 while 循环中调用并观察错误是否消失来验证这一点。

可以看到我的回答here对您引用的书中的示例进行另一种分析。

关于c# - 为什么我们需要 Thread.MemoryBarrier()?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3556351/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com