• c++多线程互斥量(mute)与原子变量(atomic)


    1、互斥量(mute)

    1.1 std::mutex

    mutex就是互斥量的意思,在c++中使用互斥量需要包含#include
    当多个线程同时范围统一个资源的的时候,就会造成线程的不安全情况。

    1.2 多个访问同个资源,线程不安全的例子

    但线程t1,t2同时操作整形globaVariableInt时,正常情况globaVariableInt++再–,理应输入的是0,当这时候我们可以看到打印的不是0,而且是1083。

    
    #include 
    #include
    #include  
    #include  //1、添加互斥量头文件
    
    
    using namespace std;
    
    int globaVariableInt = 0;
    
    void task1() {
    	for (int i = 0; i < 1000000; i++)
    	{
    		mtx1.lock();//3、上锁操作,避免其他线程访问代码
    		globaVariableInt++;
    		globaVariableInt--;
    		mtx1.unlock();//4、执行完代码,解锁操作
    	}
    }
    
    int main()
    {
    
    	std::thread t1(task1);
    	std::thread t2(task1);
    	t1.join();
    	t2.join();
    	std::cout << "当前globaVariableInt值为" << globaVariableInt << endl;
    
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    这个时候,我们就可以使用互斥量(mute)来抱住线程安全。

    1.3 使用互斥量(mute)上锁

    • 成员函数:
      lock():加锁,如果已经被其他线程加锁了,则会阻塞
      unlock():解锁,在线程没有获得锁是不能进行解锁

    在这里插入图片描述
    这个适合,我们给操作内容上添加上锁lock()和解锁unlock()。

    std::mutex mtx1;//2、创建一个全局的互斥量
    
    void task1() {
    	for (int i = 0; i < 1000000; i++)
    	{
    		mtx1.lock();//3、上锁操作,避免其他线程访问代码
    		globaVariableInt++;
    		globaVariableInt--;
    		mtx1.unlock();//4、执行完代码,解锁操作
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    1.4 std::lock_guard()与std::unique_lock

    在实际使用过程中,我们可能会因为忘记解锁的操作,就会造成死锁。所以在创建互斥量(mute)后,推荐使用 std::lock_guard()与std::unique_lock();

    • std::lock_guard()
      在构造函数中进行加锁,析构函数中进行解锁。所以我们可以在栈上创建std::lock_guard,在生命周期结束后,自动解锁。
    void task2() {
    	for (int i = 0; i < 1000000; i++)
    	{
    		//避免忘记解锁,可以使用lock_guard unique_lock
    		std::lock_guard<mutex> lock1(mtx1);
    		globaVariableInt++;
    		globaVariableInt--;
    		//生命周期结束后,自动解锁。
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • std::unique_lock()
      它比lock_guard 更加灵活,可以在声明周期内手动解锁
    void task2() {
    	for (int i = 0; i < 1000000; i++)
    	{
    		//避免忘记解锁,可以使用lock_guard unique_lock
    		//std::lock_guard lock1(mtx1);
    		std::unique_lock<std::mutex> lock2(mtx2);
    		lock2.unlock();//相比lock_guard更加灵活,可以在作用域内手动解锁
    		globaVariableInt++;
    		globaVariableInt--;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    2、 原子变量(atomic

    2.1 原子变量

    • C++11 提供了一个原子类型 std::atomic,通过这个原子类型管理的内部变量就可以称之为原子变量,我们可以给原子类型指定 bool、char、int、long、指针等类型作为模板参数(不支持浮点类型和复合类型)。
    • 原子指的是一系列不可被 CPU 上下文交换的机器指令,这些指令组合在一起就形成了原子操作。在多核 CPU 下,当某个 CPU 核心开始运行原子操作时,会先暂停其它 CPU 内核对内存的操作,以保证原子操作不会被其它 CPU 内核所干扰。
      如上的互斥量(mute)的使用可以同原子量来实现,不用上锁。

    2.2 简单使用

    
    #include 
    #include
    #include  
    #include  
    #include //1、引用原子类型
    
    using namespace std;
    
    
    //2、定义一个原子类型
    std::atomic<int> globaVariableInt2 = 0;
    //定义一个原子类型,不用上锁的方式,
    void task4() {
    	for (int i = 0; i < 1000000; i++)
    	{
    
    		globaVariableInt2++;
    		globaVariableInt2--;
    	}
    }
    
    int main()
    {
    
    	/*std::thread t1(task1);
    	std::thread t2(task1);*/
    	std::thread t1(task4);
    	std::thread t2(task4);
    	t1.join();
    	t2.join();
    	std::cout << "当前globaVariableInt2值为" << globaVariableInt2 << endl;
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    在这里插入图片描述

  • 相关阅读:
    地图瓦片下载地址汇总(leaflet 百度地图等)
    软考-防火墙技术与原理
    HPC 集群计算类型的注意事项
    理解ceres
    [word] 如何在word中插入地图? #学习方法#其他
    2023年【上海市安全员A证】报名考试及上海市安全员A证试题及解析
    扫雷游戏优化详解——c语言实现
    【Java线程池ThreadPool(创建线程的第三种方式)】
    西门子6ES72881ST200AA1
    【数据结构】二叉树的顺序结构实现及时间复杂度计算(二)
  • 原文地址:https://blog.csdn.net/weixin_43482965/article/details/126429391