1:using System;
2:using System.Threading;
3:
4:internalclass Program
5: {
6:staticbool StopWorker;
7:
8:staticvoid Main(string[] args)
9: {
10: Console.WriteLine("Main: letting worker run for 5 seconds");
11: var thread = new Thread(worker);
12: thread.Start();
13: Thread.Sleep(5000);
14: StopWorker = true;
15: Console.WriteLine("Main: waiting for worker to stop");
16: thread.Join();
17: Console.WriteLine("Main: Worker stopped");
18: Console.Read(); // Let the user view the results before closing the console window.
19: }
20:
21:staticvoid worker()
22: {
23: Int32 x = 0;
24:while (!StopWorker)
25: x++;
26: Console.WriteLine("Worker: stopped when x={0}", x);
27: }
28: }
If you put this code into a console app and run it, you may find it will work. Try setting the build configuration to ‘Release’, then go to the Debug menu and select Start Without Debugging. I’m pretty sure that under those circumstances, the program will appear to hang; the worker thread never exits. The program appears to be correct and works fine under the debugger or in a 64-bit process. Only when it is run as 32-bit and without debugging does it mysteriously fail. Can you see why? The answer is not trivial and requires quite a deep understanding of the compiler.
The key is to understand that compilers make optimizations. In a .NET program, there are 3 possible opportunities for optimization: once in the C# compiler, once in the CLR’s Just In Time (JIT) compiler and then on the CPU itself. In this particular instance, the CLR x86 JIT compiler hoists the test on line 24 out of the loop, since the value is not changed inside the loop then the compiler assumes there’s no point in testing it every time. When running under the debugger, optimizations are disabled so the code works correctly. It also turns out that the 64-bit version of the JIT compiler doesn’t have this particular optimization (it is a less mature compiler) sot he problem doesn’t happen when running as 64-bit.
Those with a C programming background will probably immediately recognise the fix: use of the volatile keyword in line 6, like so:
staticvolatilebool StopWorker;

Any public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.