在Java中,如果只有在加入突变它的线程后访问它,那么该字段不需要是易失性的;联接在关系之前强制执行。
在c#中怎么样?使用下面的代码,我保证在调用Join()之后会看到_value的更新值,或者我是否需要使_value变为volatile?
private String _value = "FOO"
public void Foo()
{
Thread myThread = new Thread(Bar);
myThread.Start();
myThread.Join();
Console.WriteLine("[Main Thread] _val = "+ _value);
}
public void Bar()
{
for(int i=0; i<1000; i++)
{
Console.WriteLine("Looping");
if(i==75)
{
_value="BAR";
}
}
Console.WriteLine("DONE Looping");
}
在我的代码段中,&#34; BAR&#34;总是打印?
答案 0 :(得分:3)
首先:我的一般经验法则是,如果我问的问题是“这需要变动吗?”那时我不太了解内存模型,不足以编写低锁代码。
只需将物品置于锁定之下,如果没有非常充分的理由和专家的建议,请不要尝试低锁定代码。
我不是这样的专家。我不太了解C#内存模型编写低锁代码,如果在弱内存模型硬件上运行它将是正确的。
解决您的实际问题:
我保证在调用Join()之后会看到_value的更新值,还是我需要使_value变为volatile?
您的问题的答案在C#规范中,为方便起见,我在此引用:
执行C#程序,以便在关键执行点保留每个执行线程的副作用。副作用定义为易失性字段的读取或写入,对非易失性变量的写入,对外部资源的写入以及抛出异常。必须保留这些副作用的顺序的关键执行点是对volatile字段,锁定语句以及线程创建和终止的引用。
您有一个非易失性变量的写入,并且该线程在连接返回时结束,因此必须在连接点保留写入的副作用。
答案 1 :(得分:1)
通用线程同步操作执行完整的内存屏障。肯定是开始和加入并结束一个线程。没有这些,各种程序都会出现故障。
这些保证通常没有记录,但实际上很明显。唉,我不能提供确凿的证据,除非说其他任何东西都会疯狂。
请参阅this list作为未充分记录的证据,并且您正在寻找的财产很可能成立。
在我的代码段中,&#34; BAR&#34;总是打印?
是。我相信所有专家都会就此达成一致。这是一个更简单的代码示例,它提出了相同的观点:
int x = 0;
Thread myThread = new Thread(() => x = 1);
myThread.Start();
myThread.Join();
x = 2;
Console.WriteLine(x); //Prints 2 because of memory barriers on exit and on Join.