Reputation: 1157
I have the following code:
public static void Main(string[] args)
{
bool isComplete = false;
var t = new Thread(() =>
{
int i = 0;
while (!isComplete) i += 0;
});
t.Start();
Thread.Sleep(500);
isComplete = true;
t.Join();
Console.WriteLine("complete!");
}
The program will hung in release mode and will give an output in debug mode ("complete!").
What is the reason for that?
Thanks.
Upvotes: 2
Views: 273
Reputation: 2719
The value of the variable isComplete
is most likely being loaded into a register in your release build to avoid the round-trip to read value from memory in the while loop.
Therefore, the loop will not "detect" when the value of isComplete
is changed to true.
You need to indicate to the compiler that this variable is volatile
: essentially telling the system to not make assumptions based on code executing in the current thread whether or not this memory changes (i.e., some other thread or process might change it).
See this SA answer for some details:
How do I Understand Read Memory Barriers and Volatile - StackOverflow
If you want to dig even deeper into memory and concurrency, then here is an excellent article by Fabian Giesen about cache coherency in multi-core systems:
Atomic operations and contention
Here are good reasons why you should just use a lock unless you know what you are doing:
Volatile keyword usage vs Lock - StackOverflow
Here is MSDN doc on Volatile.Read
method:
Note that without some more detailed explanation (see above SA thread, or google) of the terms, the description in the MSDN documentation can be hard to translate into what is actually going on.
Here is the MSDN doc on volatile
keyword:
In C# this keyword will use a half-fence (as far as I know); you will also find this keyword in C for example, but there it only affects compiler optimizations (does not insert a memory barrier) as it was originally intended for reading memory-mapped I/O.
Example:
bool isComplete = false;
var t = new Thread(() =>
{
int i = 0;
while (!Volatile.Read(ref isComplete)) i += 0;
});
t.Start();
Thread.Sleep(500);
isComplete = true;
t.Join();
Console.WriteLine("complete!");
Upvotes: 10