一.单词解释
- 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