多线程编程中的一个容易忽视的地方
前几天一直在查一个bug,在debug下程序在初始化的时候没有问题,release下程序在初始化的时候陷入了死循环,且挂载在vs上release又不会死循环,查了好久,昨天终于查到了为什么,这里暂时不提我自己做的项目,下面我写一个demo来说明这个问题:
程序如下:
1 class Program 2 { 3 private static bool s_stop = false; 4 static void Main(string[] args) 5 { 6 Console.WriteLine("Mian:letting worker run for 5 seconds"); 7 Thread t = new Thread(Worker); 8 t.Start(); 9 Thread.Sleep(5000); 10 s_stop = true; 11 Console.WriteLine("Main:waiting for worker to stop"); 12 t.Join(); 13 } 14 15 private static void Worker(object o) 16 { 17 Int32 x = 0; 18 while (!s_stop) x++; 19 Console.WriteLine("Worker:stopped when x={0}", x); 20 } 21 }
程序的目的是先让一个工作线程工作,5s后,给变量s_stop赋值,让线程的方法结束循环,程序最终会退出。
目的是好的,但是这个程序有个隐患,在debug下是没有问题的,挂调试器也是没有问题的,就是release模式下有问题,程序一直会陷入死循环。
为什么呢?
编译器会优化我们的代码,s_stop在主线程和工作线程中都要访问,这就会涉及到了数据共享问题,当编译器看到Worker方法的时候,发现里面的s_stop不会改变
,且这个值是false的,那么就把后面的代码都给优化了,因为whie(true)了。
这种错误,我们可能在编程过程中很难发现,也很难碰到,但是一旦碰到就是灾难。
我的项目中初始化的时候是这样子的:
console.Run();
while(!console.IsBusy){}
因为console.Run是一个线程,while()又是一个线程,但是一开始IsBusy是false的,所以在编译器优化的时候while就变成了死循环了。
最好的作法就是在变量前面加上Volatile关键字就可以了
https://files.cnblogs.com/files/monkeyZhong/TestVolatile.zip