C++中volatile关键字及常见的误解总结

C++中volatile关键字及常见的误解总结

什么是volatile关键字

volatile 是一个类型修饰符,用于告诉编译器,该变量可能在程序执行阶段被不由程序本身产生的修改。

通俗点讲,当我们定义一个变量时,系统会在内存中为其分配一块内存区域,我们通过对这些内存的读写来操作这些变量。但是在复杂的多线程并发编程中,可能出现另外一个线程或者硬件设备修改了这块内存区域,从而导致了程序错误。所以这时我们需要告诉编译器,这个变量可能会被意外修改,编译器就不会把这个变量缓存到寄存器中,注意到每次访问都要从内存中读取,从而减小了出错的机会。

volatile的使用

又由于volatile本身只是告诉编译器,变量有可能产生变化,并不会强制刷新,所以在多线程并发编程中,volatile关键字是不够保险的。

使用方法:

volatile int num = 0;
volatile char ch = 'a';
volatile double pi = 3.14159;

volatile的常见误解

误解1:volatile变量是线程安全的

volatile关键字并不能保证线程安全。volatile并不能保证原子性(线程的基本操作单位,具有不可分割性)。如果涉及到需要确保原子性的操作,还必须依靠更强的多线程并发编程机制,如互斥锁、自旋锁、条件变量等。

例如:

volatile int num = 0;
for(int i = 0; i < 10000; ++i){
    ++num;
}

如果两个线程同时执行这段代码,num的值是不确定的,因为volatile并不能保证原子性。

误解2:volatile变量不写入cpu缓存,每次读/写都会从内存中读取/写入

这个误解有一部分是正确的。volatile变量不会缓存,但不代表每次都会从内存中读取/写入,CPU有自己多级缓存,volatile只能保证在缓存失效的时候从内存中取值,具体在什么时候失效并不是我们可以控制/预知的,所以在使用volatile变量的时候还是要小心谨慎,避免因为缓存失效而导致的问题。

例如:

volatile int num = 0;
for(int i = 0; i < 10000; ++i){
    num += i;
}

这段代码在多次循环后,由于计算次数增加,可能会被缓存,所以不会每次都从内存中取值。

示例说明

以下是一个示例程序,该程序利用volatile关键字保证了信号量变量在多线程应用中正确的使用。

#include <iostream>
#include <thread>

using namespace std;

volatile int sem = 1;

void producer() {
    for (int i = 0; i < 1000; ++i) {
        while (sem == 0) {
            // wait until sem == 1
        }
        sem = 0;
        cout << "Producer produces " << i << endl;
    }
}

void consumer() {
    for (int i = 0; i < 1000; i++) {
        while (sem == 1) {
            // wait until sem == 0
        }
        sem = 1;
        cout << "Consumer consumes " << i << endl;
    }
}

int main() {
    thread t1(producer);
    thread t2(consumer);
    t1.join();
    t2.join();
    return 0;
}

在这个程序中,我们定义了一个信号量变量sem,并使用while循环来保证信号量的正确性。由于while循环的判断条件依赖于sem的值,在多线程编程中,如果没有使用volatile关键字,就可能会出现由于编译器优化而导致的信号量错误的情况。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C++中volatile关键字及常见的误解总结 - Python技术站

(0)
上一篇 2023年5月23日
下一篇 2023年5月23日

相关文章

  • C++实现万年历源代码

    下面为你详细讲解“C++实现万年历源代码”的完整攻略。 1. 需求分析 万年历是一款常用的日历工具,可以查询指定日期的日历信息。因此,我们需要实现以下几个功能:1. 输入年份和月份,输出该月的日历2. 根据当前时间自动输出当月的日历 2. 设计思路 我们可以根据闰年的规律和每月的天数,计算出一个月中每一天是星期几,并将这些天数以矩阵的形式输出。 3. 代码实…

    C 2023年5月24日
    00
  • 布隆过滤器(bloom filter)及php和redis实现布隆过滤器的方法

    布隆过滤器及实现方法攻略 什么是布隆过滤器? 布隆过滤器是一种非常实用的数据结构,它可以用于快速判断一个元素是否在一个集合中。布隆过滤器可以有效地降低查询一个元素是否在集合中的时间复杂度,但是会带来一定的误判率。它由早在1970年提出,以其高效的查询速度和内存占用率低的特点而广受欢迎,被广泛应用于网络爬虫等场景中。 布隆过滤器的实现原理 布隆过滤器采用的是概…

    C 2023年5月22日
    00
  • Swift如何调用Objective-C的可变参数函数详解

    那么首先我们需要了解的是Objective-C中的可变参数函数的使用方式和Swift对其的调用方式。 在Objective-C中,可变参数函数通常使用va_list和va_start、va_arg、va_end等宏来进行参数的处理。其中 va_start宏接受可变参数函数的参数列表以及可变参数的最后一个非变长参数,在获取可变参数时,需要使用 va_arg宏进…

    C 2023年5月23日
    00
  • 明日之后怎么安装C型窗 C型窗安装版方法介绍

    下面是明日之后怎么安装C型窗的完整攻略。 安装C型窗攻略 安装C型窗的方法分为以下几步: 找到C型窗安装版 下载C型窗安装版并解压 将解压后的文件放入游戏目录中 在游戏中使用命令行安装 接下来将详细介绍每一步。 1. 找到C型窗安装版 首先需要找到C型窗安装版文件,可以在明日之后的论坛或社群中寻找,也可以在百度云、360云盘等网盘中进行下载。建议下载前先阅读…

    C 2023年5月23日
    00
  • C++控制台实现密码管理系统

    为了编写C++控制台实现密码管理系统,我们需要遵循以下步骤: 步骤1:设计数据结构 设计数据结构是密码管理系统的第一步,我们需要确定各种密码信息的存储方式。我们可以选择使用结构体、类或数组来存储不同的用户信息。 例如: struct Password{ char username[15]; char password[15]; char descriptio…

    C 2023年5月23日
    00
  • C++单例模式为何要实例化一个对象不全部使用static

    C++的单例模式是一种常用的设计模式,用于确保一个类在应用程序中只存在一个实例,以及提供全局访问该实例的机制。 在C++的单例模式实现中,通常将单例类的构造函数设为私有的,以禁止其他代码直接构造其实例。然后,提供一个静态方法,用于获得该类的唯一实例。该方法将根据需要创建一个实例,并将其保存在静态成员变量中。每次调用该方法时,都会返回该唯一实例。这种实现方式的…

    C 2023年5月22日
    00
  • 三星Galaxy Book Flex值得入手吗 三星笔记本Galaxy Book Flex详细评测

    三星Galaxy Book Flex值得入手吗 三星笔记本Galaxy Book Flex详细评测 如果你正在寻找一款高性能、轻巧、功能强大的2合1笔记本,那么三星Galaxy Book Flex绝对值得一看。该笔记本采用最新一代的处理器,配备高清触摸屏和可旋转键盘,具备出色的性能和灵活的使用方式,让你随时随地体验高效便捷的计算体验。 性能和硬件 三星Gal…

    C 2023年5月22日
    00
  • Python中常见的数据类型小结

    让我来为您详细讲解“Python中常见的数据类型小结”的攻略。 一、Python常见的数据类型 Python中常见的数据类型包括数字、字符串、列表、元组、字典和集合,下面分别详细介绍。 1. 数字(Number) 在Python中,数字可以分为整数(int)、浮点数(float)、布尔值(bool)和复数(complex)4种类型。在Python中,数字类型…

    C 2023年5月22日
    00
合作推广
合作推广
分享本页
返回顶部