Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>The volatile keyword should not confused as to making _a, _b, and _c thread-safe. See <a href="https://stackoverflow.com/questions/154551/volatile-vs-interlocked-vs-lock">here</a> for a better explanation. Further, the ManualResetEvent does not have any bearing on the thread-safety of _a, _b, and _c. You have to manage that separately.</p> <p>EDIT: With this edit, I am attempting to distill all of the information that has been put in various answers and comments regarding this question.</p> <p>The basic question is whether or not the result variables (_a, _b, and _c) will be 'visible' at the time the flag variable (_completed) returns true.</p> <p>For a moment, let's assume that none of the variables are marked volatile. In this case, it would be possible for the result variables to be set <strong><em>after</em></strong> the flag variable is set in Task(), like this:</p> <pre><code> private void Task() { // possibly long-running work goes here _completed = true; _a = result1; _b = result2; _c = result3; _completedSignal.Set(); } </code></pre> <p>This is clearly not what we want, so how do we deal with this?</p> <p>If these variables are marked volatile, then this reordering will be prevented. But that is what prompted the original question - <em>are the volatiles required or does the ManualResetEvent provide an implicit memory barrier such that reordering does not occur, in which case the volatile keyword is not really necessary?</em></p> <p>If I understand correctly, wekempf's position is that the WaitOne() function provides an implicit memory barrier which fixes the problem. <strong><em>BUT</em></strong> that doesn't seem sufficient to me. The main and background threads <strong>could</strong> be executing on two separate processors. So, if Set() does not also provide an implicit memory barrier, then the Task() function could end up being executed like this on one of the processors (even with the volatile variables):</p> <pre><code> private void Task() { // possibly long-running work goes here _completedSignal.Set(); _a = result1; _b = result2; _c = result3; _completed = true; } </code></pre> <p>I have searched high and low for information regarding memory barriers and the EventWaitHandles, and I have come up with nothing. The only reference I have seen is the one wekempf has made to Jeffrey Richter's book. The problem I have with this is that the EventWaitHandle is meant to synchronize threads, not access to data. I have never seen any example where EventWaitHandle (e.g., ManualResetEvent) is used to synchronize access to data. As such, I'm hard-pressed to believe that EventWaitHandle does anything with regard to memory barriers. Otherwise, I would expect to find <strong><em>some</em></strong> reference to this on the internet.</p> <p>EDIT #2: This is a response to wekempf's response to my response... ;)</p> <p>I managed to read the section from Jeffrey Richter's book at amazon.com. From page 628 (wekempf quotes this too):</p> <blockquote> <p>Finally, i should point out that whenever a thread calls an interlocked method, the CPU forces cache coherency. So if you are manipulating variables via interlocked methods, you do not have to worry about all of this memory model stuff. Furthermore, all thread synchronization locks (<strong>Monitor</strong>, <strong>ReaderWriterLock</strong>, <strong>Mutex</strong>, <strong>Semaphone</strong>, <strong>AutoResetEvent</strong>, <strong>ManualResetEvent</strong>, etc.) call interlocked methods internally.</p> </blockquote> <p>So it would seem that, as wekempf pointed out, that the result variables do <strong><em>not</em></strong> require the volatile keyword in the example as shown since the ManualResetEvent ensures cache coherency.</p> <p>Before closing this edit, there are two additional points I'd like to make.</p> <p>First, my initial assumption was that the background thread would potentially run multiple times. I obviously overlooked the name of the class (OneUseBackgroundOp)! Given that it is only run once, it is not clear to me why the DoSomething() function calls WaitOne() in the manner that it does. What is the point of waiting initialWaitMs milliseconds if the background thread may or may not be done at the time DoSomething() returns? Why not just kickoff the background thread and use a lock to synchronize access to the results variables <strong><em>OR</em></strong> simply execute the contents of the Task() function as part of the thread that calls DoSomething()? Is there a reason not to do this?</p> <p>Second, it seems to me that not using some kind of locking mechanism on the results variables is still a bad approach. True, it is not needed in the code as shown. But at some point down the road, another thread may come along and try to access the data. It would be better in my mind to prepare for this possibility now rather than try to track down mysterious behavior anomalies later.</p> <p>Thanks to everyone for bearing with me on this. I've certainly learned a lot by participating in this discussion.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload