一.单词解释
- adj.易变的;无定性的;无常性的;可能急剧波动的
二.使用说明:
volatile表示这变量可能会被意想不到地改变,提示编译器别优化老子,编译器就不会去假设这个变量的值了。
建议你用volatile修饰在多个线程中使用的原生类型变量
举例说明:
class Gadget { public: void Wait() { while (!flag_) { Sleep(1000); // sleeps for 1000 milliseconds } }
void Wakeup() { flag_ = true; } ... private: bool flag_; };
上面代码中的Wait()想要实现每隔一秒对flag_进行判断,如果flag_被另外的线程改为true的话,就会跳出循环.
但实际上这样设计是存在问题的,原因就在于while循环中,编译器认为flag_的值不会改变,那么它会把flag_的值从内存中缓存到寄存器中,这样的话就提高了访问效率.这对于单线程是很好的优化,但是这样会让程序变得不正确.当另一个线程改变了内存中flag_的值时,while循环还是访问寄存器中的值,这样就导致了出现问题.
C和C++给你提供了显式禁用这种缓存优化的机会。如果你声明变量是使用了volatile修饰符,编译器就不会把这个变量缓存在寄存器里——每次访问都将去存取变量在内存中的实际位置。这样你要对Gadget的Wait/Wakeup做的修改就是给flag_加上正确的修饰:
class Gadget { public: ... as above ... private: volatile bool flag_; };
参考文章:https://blog.csdn.net/xuwentao37x/article/details/27804169
三代码示例
#include <iostream> #include <windows.h> using namespace std; class Test { private: volatile bool m_bFlag; //在VS下不加volatile也可以正确执行,不过为了安全还是需要加上的. public: Test() { m_bFlag = false; } void Wait() { while (!m_bFlag) { cout << "I'm Sleeping" << endl; Sleep(1000); } cout << "I'm awake" << endl; } void WakeUp() { m_bFlag = true; } }; DWORD WINAPI ThreadFun2(void *param) { Test *tThread = (Test *)param; tThread->WakeUp(); return 0; } DWORD WINAPI ThreadFun1(void *param) { Test *tThread = (Test *)param; tThread->Wait(); return 0; } //用两个子线程实现,发现两个子线程之间的生存期是相互独立的.它们只受主线程的影响. int main() { Test t; HANDLE h[2]; h[0] = CreateThread(NULL, 0, ThreadFun1, &t, 0, NULL); Sleep(10);//如果不加这个Sleep,多数情况下会先打印Sleeping,然后打印awake,但是也有情况会直接打印awake. h[1] = CreateThread(NULL, 0, ThreadFun2, &t, 0, NULL); WaitForMultipleObjects(2, h, TRUE, INFINITE); return 0; }
关于这个例子,多线程相关的探讨可以参见自己总结的这篇文章:https://i-beta.cnblogs.com/posts/edit-done;postId=12708937