Skip to content

Instantly share code, notes, and snippets.

@AArnott
Created June 25, 2012 04:23
Show Gist options
  • Save AArnott/2986510 to your computer and use it in GitHub Desktop.
Save AArnott/2986510 to your computer and use it in GitHub Desktop.
Demonstrates how the memory model in .NET requires the use of volatile fields when not using locks.
using System;
using System.Threading;
namespace MemoryModelTest {
class Program {
static Barrier barrier;
static volatile LazyExample example;
static bool exit;
static volatile CountdownEvent evt;
static void Main(string[] args) {
var threads = new Thread[Environment.ProcessorCount];
barrier = new Barrier(threads.Length + 1);
for (int i = 0; i < threads.Length; i++) {
threads[i] = new Thread(ThreadWorker);
threads[i].Start();
}
while (true) {
evt = new CountdownEvent(threads.Length);
example = new LazyExample();
barrier.SignalAndWait();
evt.Wait();
}
//Console.ReadLine();
//exit = true;
//for (int i = 0; i < threads.Length; i++) {
// threads[i].Join();
//}
}
static void ThreadWorker() {
while (!exit) {
barrier.SignalAndWait();
int value = example.GetInt();
if (value != 42) {
Console.WriteLine("Iteration {0} obtained unexpected value {1}", barrier.CurrentPhaseNumber, value);
}
evt.Signal();
}
}
}
// WARNING: Bad code
class LazyExample {
private int _value;
private bool _initialized; // fix the bug in this code by adding volatile to this field.
public int GetInt() {
if (_value < 0) throw new Exception(); // NOTE: extra reads to get _value
// pre-loaded into a register
if (!_initialized) // Read 1
{
_value = 42;
_initialized = true;
return _value; // This extra return also happens to be needed
// to demo the behavior in .NET 4.5.
}
return _value; // Read 2
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment