IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> volatile关键字 -> 正文阅读

[C++知识库]volatile关键字

volatile的作用

保存内存的可见性。告诉编译器,volatile修饰的变量,不允许优化,对该变量的任何操作都必须再真实的内存中进行操作。

volatile应用场景

懒汉方式-单例模式

懒汉方式中的静态对象指针要加volatile关键字防止过度优化出现问题。

// 懒汉模式, 线程安全
template <typename T>
class Singleton {
    volatile static T* inst; // 需要设置 volatile 关键字, 否则可能被编译器优化.
        static std::mutex lock;
    public:
    static T* GetInstance() { //1.同时有多个线程进来 2. 不同时
            if (inst == NULL) { // 双重判定空指针, 降低锁冲突的概率, 提高性能.
                    lock.lock(); // 使用互斥锁, 保证多线程情况下也只调用一次 new. 
                    if (inst == NULL) {
                        inst = new T();
                    }
                lock.unlock();
            }
        return inst;
    }
};

new底层调用为operator new构造函数operator new底层调用malloc,失败了抛异常。当调用结束后返回内存地址赋值给inst。

 inst = new T();

而问题处在于初始化和赋值地址会出现重排序(重排序是指编译器和处理器为了优化程序性能而对指令序列进行重新排序的一种手段)既可能会出现重排序从申请空间,构造函数初始化后再赋值地址排序变为申请空间,赋值地址后再构造函数初始化,那么在多线程的情况下就会导致出现问题。

线程A执行到new(),开始初始化实例对象,由于存在指令重排序,这次new操作先执行了3把地址赋值了,还没有执行2初始化实例。这时时间片结束了,切换到线程B执行,线程B调用new T()后发现地址不等于null,便直接返回引用地址了,这样会导致线程B访问到了一个还未初始化的对象。

捕捉信号修改全局变量

#include<iostream>
#include<cstdio>
#include<signal.h>
using namespace std;

int quit = 0;

void handler(int sig) {
     printf("set quit = 1\n");
     quit = 1;
}
int main() {
    signal(2, handler);
    while(!quit);
    return 0;
}

当我们在函数中使用一些不会修改的变量时,编译器可以对其进行优化。因为数据不会改变,我们可以让CPU在运算时,只要在寄存器里拿数据就好了,就不用到内存拿数据,效率会高很多。因此此时编译器认为main函数中的quit关键字一直没变,存在寄存器中。而handler()中存的变量在内存中,改变对寄存器中该变量不会产生影响。

image-20220315195859580

针对这种情况我们对该全局变量加volatile关键字即可。

#include<iostream>
#include<cstdio>
#include<signal.h>
using namespace std;

volatile int quit = 0;

void handler(int sig) {
     printf("set quit = 1\n");
     quit = 1;
}
int main() {
    signal(2, handler);
    while(!quit);
    return 0;
}

image-20220315200254605

此时正常结束。

总结

总结:volatile修饰的变量,不允许编译器优化,对该变量的任何操作都必须在真实的内存中进行操作。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-03-17 21:52:22  更:2022-03-17 21:56:14 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 2:29:12-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码