Actually, volatile
doesn't help here so I don't think I know what is happening here.
Here is a little part of C# - The C# Memory Model in Theory and Practice:
public class DataInit {
private int _data = 0;
private bool _initialized = false;
void Init() {
_data = 42; // Write 1
_initialized = true; // Write 2
}
void Print() {
if (_initialized) // Read 1
Console.WriteLine(_data); // Read 2
else
Console.WriteLine("Not initialized");
}
}
Suppose Init and Print are called in parallel (that is, on different threads) on a new instance of DataInit. If you examine the code of Init and Print, it may seem that Print can only output “42” or “Not initialized.” However, Print can also output “0.”
The C# memory model permits reordering of memory operations in a method, as long as the behavior of single-threaded execution doesn’t change. For example, the compiler and the processor are free to reorder the Init method operations as follows:
void Init() {
_initialized = true; // Write 2
_data = 42; // Write 1
}
This reordering wouldn’t change the behavior of the Init method in a single-threaded program. In a multithreaded program, however, another thread might read _initialized and _data fields after Init has modified one field but not the other, and then the reordering could change the behavior of the program. As a result, the Print method could end up outputting a “0.”
The reordering of Init isn’t the only possible source of trouble in this code sample. Even if the Init writes don’t end up reordered, the reads in the Print method could be transformed:
void Print() {
int d = _data; // Read 2
if (_initialized) // Read 1
Console.WriteLine(d);
else
Console.WriteLine("Not initialized");
}
Just as with the reordering of writes, this transformation has no effect in a single-threaded program, but might change the behavior of a multithreaded program. And, just like the reordering of writes, the reordering of reads can also result in a 0 printed to the output.