Reputation: 465
I try to understand the use of volatile in multithread context. In the following code from another source of knowledge on the Internet:
class Program
{
static string _result;
//static volatile bool _done;
static bool _done;
static void SetVolatile()
{
// Set the string.
_result = "Dot Net Perls";
// The volatile field must be set at the end of this method.
_done = true;
}
static void Main()
{
// Run the above method on a new thread.
new Thread(new ThreadStart(SetVolatile)).Start();
// Wait a while.
Thread.Sleep(200);
// Read the volatile field.
if (_done)
{
Console.WriteLine(_result);
}
}
}
The demonstrated use of a volatile keyword should prevent a thread from reading a value stored in a cache. Instead of this it should check an actual value.
So without a volatile _done
should still have a false value (read from the cache) and Console.WriteLine
statement should not be executed.
Unfortunately running this code in Debug/Release mode without a volatile keyword always produces the output. What is the point of this particular example?
Upvotes: 2
Views: 306
Reputation: 673
It's better to read ECMA-335, §I.12.6
Main points are:
So, in this case, your code can be optimized.
Try the following code:
private bool flag = true;
public void LoopReadHoistingTest()
{
Task.Run(() => { flag = false; });
while (flag)
{
// Do nothing
}
}
In Debug mode (without optimizations) it will work fine. In Release mode (with optimizations) it will hang forever, because moving the read outside the loop is quite common optimization.
But if you mark field volatile (or use Volatile.Read
method, or some of Interlocked
methods) it will work because in that case optimizations are prohibited.
In your example (without loop), Thread.Sleep
makes an implicit memory barrier (because it's not forbidden and it makes code to work with less surprises), so it will read the value from memory. But I don't see any specification saying that it has to do an implicit memory barrier, so in some implementations it can be not true (or we have to find it in specification).
Upvotes: 0
Reputation: 101483
As already said, not using volatile
keyword does not mean all reads will necessary be cached in all circumstances. They may be cached, or may be not. But if you want more reproducable example, try this:
class Program {
static string _result;
//static volatile bool _done;
static bool _done;
static void SetVolatile() {
// Set the string.
_result = "Dot Net Perls";
// The volatile field must be set at the end of this method.
_done = true;
}
static void Main() {
// Run the above method on a new thread.
new Thread(new ThreadStart(SetVolatile)).Start();
// prevent compiler to throw away empty while loop
// by doing something in it
int i = 0;
while (!_done) {
i++;
}
Console.WriteLine("done " + i);
}
}
Here you repeatedly read _done
in a while loop, increasing possibility it will be cached. Program should terminate with "done" message but will not, because change to _done
from another thread will not be noticed.
Upvotes: 3