谈谈C++的volatile关键字以及常见的误解
作者:。)
按照C++标准,这是volatile唯一的功能,但是在一些编译器(如,MSVC)中,volatile还有线程同步的功能,但这就是编译器自己的拓展了,并不能跨平台应用。
对volatile常见的误解
实际上“volatile可以在线程间同步”也是比较常见的误解。比如以下的例子:
class AObject { public: void wait() { m_flag = false; while (!m_flag) { this_thread::sleep(1000ms); } } void notify() { m_flag = true; } private: volatile bool m_flag; }; AObject obj; ... // Thread 1 ... obj.wait(); ... // Thread 2 ... obj.notify(); ...
对volatile有误解的人,或者对并发编程不了解的人可能会觉得这段逻辑没什么问题,可能会认为volatile保证了,wait()对m_flag的读取,notify()对m_flag的写入,所以Thread 1能够正常醒来。实际上并不是,Thread 1可能永远看不到m_flag变成true。因为在多核CPU中,每个CPU都有自己的缓存。缓存中存有一部分内存中的数据,CPU要对内存读取与存储的时候都会先去操作缓存,而不会直接对内存进行操作。所以多个CPU“看到”的内存中的数据是不一样的,这个叫做内存可见性问题(memory visibility)。并发编程下,一个程序可以有多个线程在不同的CPU核中同时运行,这个时候内存可见性就会影响程序的正确性。放到例子中就是,Thread 2修改了m_flag对应的内存,但是Thread 1在其他CPU核上运行,而两个CPU缓存和内存没有做同步,导致Thread 1运行的核上看到的一直都是旧的数据,于是Thread 1永远都不能醒来。内存可见性问题不是多线程环境下会遇到的唯一的问题,CPU的乱序执行也会导致一些意想不到的事情发生,关于这点volatile能做的也是有限的。这些都是属于并发编程的内容,在此我就不多做展开,总之volatile关键字对并发编程基本是没有帮助的。
那么用不了volatile,我们该怎么修改上面的例子?C++11开始有一个很好用的库,那就是atomic类模板,在<atomic>头文件中,多个线程对atomic对象进行访问是安全的。以下为修改后的代码:
class AObject { public: void wait() { m_flag = false; while (!m_flag) { this_thread::sleep(1000ms); } } void notify() { m_flag = true; } private: atomic<bool> m_flag; };
只要把“volatile bool”替换为“atomic<bool>”就可以。<atomic>头文件也定义了若干常用的别名,例如“atomic<bool>”就可以替换为“atomic_bool”。atomic模板重载了常用的运算符,所以atomic<bool>使用起来和普通的bool变量差别不大。一些atomic的高级用法,由于要涉及到C++的内存模型与并发编程,我就不在此展开了,以后有时间再补上。
相关文章:
-
-
libpointmatcher介绍: libpointmatcher is a modular library implementing the I... [阅读全文]
-
Java原子类中CAS的底层实现 从Java到c++到汇编, 深入讲解cas的底层原理. 介绍原理前, 先来一个Demo 以AtomicBoolea... [阅读全文]
-
近期看到C++标准中对volatile关键字的定义,发现和java的volatile关键字完全不一样,C++的volatile对并发编程基本没有帮助... [阅读全文]
-
前言: 最近也是期末了,有好多好多文档和实验报告要交,所以都没啥时间写文,这段时间清闲了,来补一下之前学习时遗漏的一些知识树,话说就没人吐槽这个JS... [阅读全文]
-
版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。
上一篇: 洛谷 P1965 转圈游戏
下一篇: drf框架中所有视图及用法
发表评论