邯郸wap网站建设费用,好网站目录,互动平台是什么,西安北郊网站维护运营目录
实例1
实例2
实例3
内存序中两个最重要的概念
补记
结论 实例1
看下面的例子#xff1a;在vs2013中建立如下工程#xff1a;
#include thread
#include iostream
#include chronobool done false;void worker(){std::this_thread::sle…目录
实例1
实例2
实例3
内存序中两个最重要的概念
补记
结论 实例1
看下面的例子在vs2013中建立如下工程
#include thread
#include iostream
#include chronobool done false;void worker(){std::this_thread::sleep_for(std::chrono::seconds(3));done true;
}int main(){std::thread workerThread(worker);while (!done){//std::cout waiting std::endl;不知为什么加上这句打印就可以跳出循环}std::cout thread finished std::endl;workerThread.join();std::cin.get();return 0;
}使用release模式编译该工程且优化采用O2
结果是程序卡住了本来3秒钟就可以跳出while(!done)死循环现在一直停着。 vs2013 release 模式下多线程卡住演示 实例2
实例1为什么会卡住呢因为编译器在打开优化的情况下“武断”的认为done永远都是false。编译器意识不到子线程也在改变done的取值所以就认为done是常量而不会在运行到while()时读取done的实际值于是while(!done)就变成了while(true)。假如采用debug模式编译编译器不会“自作聪明”程序在运行时将老老实实的不断读取done的实时值直到donetrue为止。所以debug模式不会卡住而release模式会卡住除非在编译时禁止优化。
现在稍微修改一下代码用volatile修饰 bool done。即使在release下编译程序仍然正常走完不卡住 这是因为volatile在提示编译器done是一个不能被优化掉的变量。即使在release模式下每次走到while时也要查看done的值。
回到原子变量的话题上来再看第三个例子
实例3
现在用atomicbool代替volatile bool。
#include thread
#include iostream
#include chrono
#include atomicstd::atomicbool done(false);void worker() {std::this_thread::sleep_for(std::chrono::seconds(3));done.store(true);
}int main() {std::thread workerThread(worker);while (!done.load()) {//std::cout waiting std::endl;不知为什么加上这句打印就可以跳出循环}std::cout thread finished std::endl;workerThread.join();std::cin.get();return 0;
}结果与使用volatile一样while(!done.load())不会一直阻塞程序向下运行 看到了上面的三个例子接下来就要请出原子变量里面一个非常重要的概念内存序。
内存序中两个最重要的概念
std::memory_order - cppreference.com列出了C11标准里的六个内存序(memory_order)。这6个内存序的作用将在下一篇文章描述。其中多次出现一个词汇“可见”(visible) 以MEMORY_ORDER_ACQUIRE为例其中文解释是 有此内存定序的加载操作在其影响的内存位置进行获得操作当前线程中读或写不能被重排到此加载之前。其他线程的所有释放同一原子变量的写入能为当前线程所见 “其他线程的所有释放同一原子变量的写入能为当前线程所见”这句话的意思是什么我们只要理解了什么是不可见就理解了可见。对照实例1在不使用volatile的情况下子线程workerThread对done的修改没有影响到主线程的while(!done)逻辑这就是说子线程对done的写入不为主线程可见。
反观实例2和3子线程修改了done主线程的运行随之受了影响这就是可见。
内存序里有两个重要的概念一是指令排序在下一篇里面将介绍另一个就是可见性visibility。从例子2和3可见在可见性这方面原子变量的作用与volatile是一样的原子变量被一个线程改变后另一个线程肯定会发现这个变化。
补记
实例123都是在vs2013下演示的。在vs2019下实例1效果有区别vs2019的while循环在示例1中同样不会阻塞。这是因为不同的编译器遵循不同的c标准。即使遵循相同的C标准不同的编译器对同一个标准的贯彻程度也不一样有的编译器严格遵循标准既不多做也不少做有的编译器在某些次要方面可能达不到C标准的要求少做还有的编译器为了安全性等方面考虑甚至超过了C标准的要求多做。vs2019很可能属于超过标准这一种即使是多线程编程中使用普通的非原子变量非易失(volatile)变量仍能保证可见性个人猜测。
但无论如何本文探讨的是C11标准的内存序不针对具体编译器。在遵守C11标准方面VS2013似乎比2019更贴切所以三个实例均采用2013示范。
结论
前文解释了原子变量的第一个功能它相当于粒度很细的互斥锁。本文介绍了原子变量的第二个功能可见性。原子变量保证了多线程编程中的可见性。事实上原子变量共负担了三个功能。下一篇文章将介绍其第三个功能--限制指令重排。