C语言关键词volatile的用法探析.docx
《C语言关键词volatile的用法探析.docx》由会员分享,可在线阅读,更多相关《C语言关键词volatile的用法探析.docx(7页珍藏版)》请在优知文库上搜索。
1、学C语言时有一个奇怪的关键字volatile,这到底有什么用呢?volatile与编译器首先来看这样一段代码:intbusy=l;voidwait()while(busy)j)编译一下,注意,这里使用02优化:AaSMod+AddE.。VVe.86f CMMO F :3.L2:“t*tx. mW6 E7 uy:8 .1O0y)让我们仔细看看生成的这段汇编:Waitzmoveax,DWORDPTRbusy|rip.L2:testeax,eaxjne.L2retbusy:.Iongl其中L2这一段即为WhiIe循环,这段指令是经过编译器优化的,可以看到,决定能否跳出循环是通过检查寄存器eax来完成
2、的,而没有检查变量busy所在内存的真实内容。注意,对于这段代码来说这里的优化是正确的,但问题是如果还有其它代码修改了变量busy,那么这里的优化会导致其它代码对变量busy的修改根本就不能生效,就像这样:intbusy=l;该函数在A线程中执行voidwait()while(busy);)该函数在B线程中执行VoidsignalObusy=O;)如果Wait函数中While循环对应的机器指令仅仅从寄存器中读取数据那么即使B线程的signal函数修改了busy变量也不能让wait函数从循环中跳出来。如果你对busy变量使用volatile修饰,生成的指令就变成这样了:A8SmyX;4.Outp
3、ut.*TfiltwBioraneiAddnew.ZAddtooL-1voidwit()Il*it:wait:.L2:moveax,DWORDPTRbusyrip|testeax,eaxjne.L2retbusy:.longl注意看此时L2这一段,每次都从busy变量所在的内存中读取数据并存放在eax,然后再去判断,这样就能确保每次都能读取到busy变量的最新值。实际上你可以把寄存器eax当做busy所在内存的cache,当CaChe(寄存器)和内存中的数据一致时不会有任何问题,但当cache与内存中的数据不一致时(也就是内存已被更新但cache保存的还是旧数据),程序的运行往往出乎预料除了多
4、线程的例子,还有一类就是signalhandler以及硬件修改该变量(用C语言与硬件交互式时经常遇到),如果编译器生成文章开头那样的指令那么等待线程将检测不到Signalhandler或者硬件对变量的修改。 DOoooosy buMemory国Hardware因此在这里我们需要告诉编译器:“不要耍小聪明,不要只从寄存器中读数据,这个变量可能在其它地方已经被修改了,使用时从内存中获取最新数据”。现在是时候简单总结一下了,volatile仅仅阻止编译器试图去优化对变量的读取操作。volatile与多线程一定要注意VoIatile仅仅确保变量的可见性,但和变量的原子访问没有半毛钱关系,这是两个完全不
5、同的任务。假设有一个非常复杂的结构体StrUCtfoo:Structdatainta;intb;intc;.;Volatilestructdatafbo;voidthread1()foo.a=1;fbo.b=2;fbo.c=3;.voidthread2()inta=fbo.a;intb=foo.b;intc=fbo.c;.你仅仅用volatile去修饰变量foo只是确保了当该变量被thread1修改后我们能在thread2中读取到最新值,但是这解决不了多线程并发读写需要原子访问foo的问题。确保变量原子性访问一般都采用锁,当使用锁时,锁本身就包含了volatile提供能力,即,确保变量的可见性
6、,因此当使用锁时没有必要使用volatile。volatile与memoryorder有的同学可能会想如果我想用VoIatiIe修饰的变量没有那么复杂,仅仅是一个int,就像这样:Volatileintbusy=O;A线程读取busy变量,B线程更新busy变量,当A检测到busy变化后执行特定操作,这样可行吗?既然通过VOlatiIe修饰后可以确保每次都从内存中读取busy,那么应该可以这样使用吧。然而,计算机在概念上可能相对简单些,但在工程实践中是复杂的。我们知道由于CPU与内存之间的速度差异非常大,CPU与内存之间有一层cache,CPU其实并没有直接读取内存,CaChe的存在会让问题复
7、杂起来,限于篇幅与本文主题这里不再展开。为优化内存读写,CPU可能会对内存读写操作进行指令重排,reordering,带来的后果就是:假设在线程1中先后执行第N行代码与第N+1行代码,但在线程2看来却是第N+1行代码先生效,假设X的初始值为O,Y的初始值为1:线程1线程2X=10if(!busy)busy=0;Y=X;当线程2检测到busy为0后读取X的值,此时读取到的X值可能为0。为解决这一问题,我们需要的不是volatile,volatile解决不了reordering问题,我们需要的是内存屏障,Inemorybarriero内存屏障是一类机器指令,该指令对处理器在该屏障指令之前与之后的内
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 语言 关键词 volatile 用法 探析
