这期内容当中小编将会给大家带来有关c#实现动荡的关键字的拾遗补漏,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。
要理解c#中的<代码> 代码>波动关键字,就要先知道编译器背后的一个基本优化原理,比如对于下面这段代码:
公共类的例子 { 公共int x; 公共空间DoWork () { x=5; var y=x + 10; Debug.WriteLine (x=?+ x +“y=?+ y); } }
在释放模式下,编译器读取<代码> x=5> 代码后紧接着读取<代码> y=x + 10> 代码,在单线程思维模式下,编译器会认为y <代码> 代码>的值始终都是<代码> 15> 代码。所以编译器会把<代码> y=x + 10> 代码优化为<代码> y=15> 代码,避免每次读取y <代码> 代码>都执行一次<代码> x + 5> 代码。但x <代码> 代码>字段的值可能在运行时被其它的线程修改,我们拿到的<代码> y> 代码值并不是通过最新修改的x <代码> 代码>计算得来的,<代码> y> 代码的值永远都是<代码> 15> 代码。
也就是说,编译器在释放模式下会对字段的访问进行优化,它假定字段都是由单个线程访问的,把与该字段相关的表达式运算结果编译成常量缓存起来,避免每次访问都重复运算。但这样就可能导致其它线程修改了字段值而当前线程却读取不到最新的字段值。为了防止编译器这么做,你就要让编译器用多线程思维去解读代码。告诉编译器字段的值可能会被其它线程修改,这种情况不要使用优化策略。而要做到这一点,就需要使用<代码> 代码>波动关键字。
给类的字段添加<代码> 代码>波动关键字,目的是告诉编译器该字段的值可能会被多个独立的线程改变,不要对该字段的访问进行优化。
使用<代码> 代码>波动可以确保字段的值是可用的最新值,而且该值不会像非波动<代码> 代码>字段值那样受到缓存的影响。好的做法是将每个可能被多个线程使用的字段标记为<代码>挥发性> 代码,以防止非预期的优化行为。
为了加深理解,我们来看一个实际的例子:
公共类工人 { 私人bool _shouldStop; 公共空间DoWork () { bool工作=false;//注意:这里会被编译器优化时为(真正的) 而(! _shouldStop) { 工作=!工作;//做某事 } Console.WriteLine(“工作线程:正在终止…“); } 公共空间RequestStop () { _shouldStop=true; } } 公共类项目 { 公共静态void Main () { var工人=新工人(); Console.WriteLine(“主线程:启动工作线程…“); var workerTask=Task.Run (worker.DoWork);//等待500毫秒以确保工作线程已在执行 thread . sleep (500); Console.WriteLine(“主线程:请求终止工作线程…“); worker.RequestStop ();//待待工作线程执行结束 workerTask.Wait ();//workerThread.Join (); Console.WriteLine(“主线程:工作线程已终止“); } }
在这个例子中,<代码>,(! _shouldStop)> 代码会被编译器优化为<代码>,(真正的)> 代码。我们可以看一下实际的运行效果来验证这一点。切换版模式,按Ctrl + F5运行程序,运行效果始终如下:
程序运行后,虽然主线程在500毫秒后执行<代码> RequestStop() 代码>方法修改了<代码> _shouldStop> 代码的值,但工作线程始终都获取不到<代码> _shouldStop> 代码最新的值,也就永远都不会终止<代码>,代码>循环。
我们修改一下程序,对<代码> _shouldStop> 代码字段加上波动<代码> 代码>关键字:
公共类工人 { 私人bool _shouldStop波动; 公共空间DoWork () { bool工作=false;//获取的是最新的_shouldStop值 而(! _shouldStop) { 工作=!工作;//做某事 } Console.WriteLine(“工作线程:正在终止…“); }//?略) }
此时在主线程调用<代码> RequestStop() 代码>方法后,工作线程便立即终止了,运行效果如下图所示:
这说明加了<代码> 代码>波动关键字后,程序可以实时读取到字段的最新值。
注意,一定要切换为释放模式运行才能看到