出国 连平县 大名县 梓潼县 拉孜县 丹东市 高邮市 特克斯县 宣汉县 亳州市 长泰县 南阳市 沂源县 原平市 来安县 高碑店市
作者:百年不渡  分类:未来幻想  点击:499次  下载:6823次  大小:70M  日期:2018-09-21

零之使魔_谈谈C++的volatile关键字以及常见的误解

标签:热销 安阳县

转载请保留以下声明
  作者:赵宗晟
  出处:https://www.cnblogs.com/zhao-zongsheng/p/909游戏人生_2018年最新新闻网2520.html

近期看到C++标准中对volatile关键字的定义,发现和java的volatile关键字完全不一样,C++的volatile对并发编程基本没有帮助。网上也看到很多关于volatile的误解,于是决定写这篇文章详细解释一下volatile的作用到底是什么。

编译器对代码的优化

在讲volatile关键字之前,先讲一下编译器的优化。

int main() {
    int i = 0;
    i++;
    cout << "hello world" << endl;
}

按照代码,这个程序会在内存中预留绿光森林_2018年最新新闻网int大小的空间,初始化这段内存为0,然后这段内存中的数据加1,最后输出“hello world”到标准输出中。但是根据这段代码编译出来的程序(加-O2选项),不会预留int大小的内存空间,更不会对内存中的数字加1。他只会输出“hello world”到标准输出中。

其实不难理解,这个是编译器为了优化代码,修改了程序的逻辑。实际上C++标准是允许写出来的代码和实际生成的程序不一致的。虽说优化代码是件好事情,但是也不能让编译器任意修改程序逻辑,不然的话我们没办法写可靠的程序了。所以C++对这种逻辑的改写是有限制的,这个限制就是在编译器修改逻辑后,程序对外界的IO依旧是不变的。怎么理解呢?实际上我们可以把我们写出来的程序看做是一个黑匣子,如果按照相同的顺序输入相同的输入,他就每次都会以同样的顺序给出同样的输出。这里的输入输出包括了标准输入输出、文件系统、网络IO、甚至一些system call等等,所有程序外部的事物都包含在内。所以对于程序使用者来说,只要两个黑匣子的输入输出是完全一致的,那么这两个黑匣子是一致的,所以编译器可以在这个限制下任意改写程序的逻辑。

volatile关键字的作用

不知道有没有注意到,刚刚提到输入输出的时候,并没有提到内存,事实上,程序对自己内存的操作不属于外部的输入输出。这也是为什么在上述例子中,编译器可以去除对i变量的操作。但是这又会出现一个麻烦,有些时候操作系统会把一些硬件映射到内存上,让程序通过对内存的操作来操作这个硬件,比如说把磁盘空间映射到内存中。那么对这部分内存的操作实际上就属于对程序外部的输入输出了。对这部分内存的操作是不能随便修改顺序的,更不能忽略。这个时候volatile就可以派上用场了。按照C++标准,对于glvalue的volatile变量进行操作,与其他输入输出一样,顺序和内容都是不能改变的。这个结果就像是把对volatile的操作看做程序外部的输入输出一样。(glvalue是值类别的一种,简单说就是内存上分配有空间的对象,更详细的请看我的另一篇文章。)

按照C++标准,这是volatile唯一的功能,但是在一些编译器(如,MSVC)中,volatil冰与火之歌第一季_2018年最新新闻网e还有线程同步的功能,但这就是编译器自己的拓展了,并不能跨平台应用。

对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();
天网_2018年最新新闻网...

对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”。西门无恨_2018年最新新闻网atomic模板重载了常用的运算符,所以atomic<bool>使用起来和普通的bool变量差别不大。一些atomic的高级用法,由于要涉及到C++的内存模型与并发编程,我就不在此展开了,以后有时间再补上。大头儿子小头爸爸_2018年最新新闻网

当前文章:http://e6qf-tousuhou-com.ansale.cn/p6b5o/4fe46_153771.html

发布时间:2018-09-21 00:58:16

银河娱乐手机版yh163am.com  2288银河主站线路检测  李晨得奖 范冰冰深夜现身:我哥  银河娱乐手机版yh163am.com停车杆砸人致死 家属索赔161万元  澳门银河真人yh163am.com  银河优越会会员申请  澳门银河yh7788.bet  澳门银河真人yh163am.com  澳门银河2949所有网址  SpaceX飞船返航:龙飞船回收情况良好收获颇丰  

您可能还对以下电子书感兴趣

零之使魔TXT下载声明:

1 2018年最新新闻网免费提供的零之使魔,均由网友上传,供下载测试之用,不作商业用途,下载后请二十四小时后删除!

2 我们根据txt小说全文所整理出零之使魔txt电子书全集免费下载,由程序自动生成零之使魔txt下载文件。

3 书友所发表的txt小说零之使魔的相关评论,并不代表本站赞同零之使魔txt下载或者支持零之使魔的读者观点。

4 如果发现小说《零之使魔txt全集》无法下载未及时更新请联系我们。如果您喜欢零之使魔txt电子书,请支持作者到书店购买正版图书。感谢您的合作与支持。

5 好看的小说