简体   繁体   English

如果我调用Thread.Join(),我是否需要volatile?

[英]Do I need volatile if I call Thread.Join()?

In Java, a field doesn't need to be volatile if you access it only after joining on the thread that mutated it; 在Java中,如果只在加入突变它的线程后才访问它,则字段不需要是volatile; the join enforces a happens before relationship. 联接在关系之前强制执行。

What about in c#? 在c#怎么样? With the below code, am I guaranteed to see the updated value of _value after calling Join() or do I need to make _value volatile ? 使用下面的代码,我保证在调用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");
}

In my code snippet, will "BAR" always be printed? 在我的代码片段中,是否会打印“BAR”?

First off: My general rule of thumb is if I am asking the question "does this need to be volatile?" 首先:我的一般经验法则是,如果我问的问题是“这是否需要波动?” then I do not understand the memory model well enough to be writing low-lock code. 那时我不太了解内存模型,不足以编写低锁代码。

Just put the thing under a lock and do not attempt low-lock code without an extraordinarily good reason and the advice of an expert. 只需将物品置于锁定之下,如果没有非常好的理由和专家的建议,请不要尝试低锁定代码。

I am not such an expert. 我不是这样的专家。 I don't know nearly enough about the C# memory model to write low-lock code with any confidence that it will be correct if run on weak memory model hardware. 我不太了解C#内存模型来编写低锁代码,如果在弱内存模型硬件上运行它将是正确的。

To address your actual question: 要解决您的实际问题:

am I guaranteed to see the updated value of _value after calling Join() or do I need to make _value volatile ? 我保证在调用Join()之后会看到_value的更新值,还是需要使_value变为volatile?

The answer to your question is in the C# specification, which I quote here for your convenience: 您的问题的答案在C#规范中,为方便起见,我在此引用:

Execution of a C# program proceeds such that the side effects of each executing thread are preserved at critical execution points. 执行C#程序,以便在关键执行点保留每个执行线程的副作用。 A side effect is defined as a read or write of a volatile field, a write to a non-volatile variable, a write to an external resource, and the throwing of an exception. 副作用定义为易失性字段的读取或写入,对非易失性变量的写入,对外部资源的写入以及抛出异常。 The critical execution points at which the order of these side effects must be preserved are references to volatile fields, lock statements, and thread creation and termination. 必须保留这些副作用的顺序的关键执行点是对volatile字段,锁定语句以及线程创建和终止的引用。

You have a write to a non-volatile variable, and the thread is ended by the time the join returns, so the side effect of the write must be preserved at the point of the join. 您有一个非易失性变量的写入,并且该线程在连接返回时结束,因此必须在连接点保留写入的副作用。

Common thread synchronization actions perform a full memory barrier. 通用线程同步操作执行完整的内存屏障。 Start and Join and ending a thread surely are among them. 肯定是开始和加入并结束一个线程。 Without that all kinds of programs would malfunction. 没有这些,各种程序都会出现故障。

These guarantees often are not documented but are practically evident. 这些保证通常没有记录,但实际上很明显。 Alas, I cannot provide hard evidence except to say that anything else would be crazy. 唉,我不能提供确凿的证据,除非说其他任何东西都会疯狂。

See this list as evidence that this is not documented well and that the property you are looking for very likely holds. 将此列表视为未充分记录的证据,并且您正在寻找的属性很可能成立。

In my code snippet, will "BAR" always be printed? 在我的代码片段中,是否会打印“BAR”?

Yes. 是。 I believe all experts would agree on that. 我相信所有专家都会就此达成一致。 Here's a simpler code sample that makes the same point: 这是一个更简单的代码示例,它提出了相同的观点:

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.

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM