【C++】详解std::mutex

2023年9月11日,周一中午开始

2023年9月11日,周一晚上23:25写完


目录


概述

std::mutex是C++标准库中提供的一种同步原语,用于保护共享资源的访问

std::mutex通过锁定互斥锁来实现对共享资源的保护。当一个线程获取了互斥锁后,其他线程必须等待该互斥锁被释放才能继续访问共享资源 。这样可以确保在同一时刻只有一个线程能够访问共享资源,从而避免了多个线程同时访问同一资源而导致的数据竞争不一致问题。

头文件

#include<mutex>

std::mutex类的成员

类型

native_handle_type 原生的互斥锁句柄类型

方法

(constructor) 构造互斥锁(公共函数)

lock 锁上互斥锁(公共函数)

try_lock 如果互斥锁没锁上就锁上互斥锁(公共函数)

unlock 解除互斥锁(公共函数)

native_handle 获取原生的互斥锁句柄(公共函数)

没有std::mutex会产生什么问题

问题一:数据竞争

在这个程序中,共享资源是count,count的值为0

按理来说,一个线程给count增加100000,另一个线程给count减去100000,最后的count应该还是0,但是事实上却不是这样,这就是数据竞争所造成的。

cpp 复制代码
#include <iostream>
#include <mutex>
#include <thread>

int count = 0;

void thread1() {
  for(int i = 0; i < 100000; i++) {
  count++; 
  }
}

void thread2() {
  for(int i = 0; i < 100000; i++) {  
  count--;
  }
}

int main() {
  std::thread t1(thread1);
  std::thread t2(thread2);

  t1.join();
  t2.join();

  std::cout <<"count:"<< count << std::endl; // 可能打印非0值
}

问题二:不一致

多个线程对共享数据进行操作,由于缓存一致性问题,可能导致其中一个线程看到的数据不是最新值。

比如在这个程序里面,有时候判断x==1是true,有时候判断x==1是false

cpp 复制代码
#include <iostream>
#include <mutex>
#include <thread>

int x = 0;

void thread1() {
  x = 1; 
}

void thread2() {
  if(x == 1)
	std::cout << "x is 1" << std::endl; 
  else
	std::cout << "x is not 1" << std::endl; 
}

int main() {
  std::thread t1(thread1);
  std::thread t2(thread2);

  t1.join();
  t2.join();

}

lock和unlock

通过lock/unlock可以保证任何时刻只有一个线程在访问共享资源,从而避免数据竞争问题。

cpp 复制代码
#include <iostream>
#include <mutex>
#include <thread>

std::mutex mutex;
int count = 0;

void thread1() {
  for(int i = 0; i < 100000; i++) {
	  // 锁定互斥锁
	  mutex.lock();  
	  
	  count++; 
	  
	  // 解锁互斥锁
	  mutex.unlock();
  }
}

void thread2() {
  for(int i = 0; i < 100000; i++) {  
	  // 锁定互斥锁
	  mutex.lock();  
	  
	   count--;
	   
	  // 解锁互斥锁
	  mutex.unlock();
  }
}

int main() {
  std::thread t1(thread1);
  std::thread t2(thread2);

  t1.join();
  t2.join();

  std::cout <<"count:"<< count << std::endl; 
}

可以看到数据竞争的问题得到了解决

死锁

死锁(Deadlock)是多线程或多进程编程中的一个严重问题,它会导致程序无法继续执行下去,因为一组线程或进程相互等待对方释放资源,但永远无法满足条件,从而陷入僵局。

死锁的定义是:多个线程因为抢占和持有资源而造成的一种互相等待的僵局状态

cpp 复制代码
#include <iostream>
#include <thread>
#include <mutex>

std::mutex mutex1;
std::mutex mutex2;

void threadA() {
    std::cout << "Thread A: Attempting to lock mutex1..." << std::endl;
    mutex1.lock();
    std::this_thread::sleep_for(std::chrono::milliseconds(1));

    std::cout << "Thread A: Attempting to lock mutex2..." << std::endl;
    mutex2.lock();
    
    // 执行任务...
    
    mutex2.unlock();
    mutex1.unlock();
}

void threadB() {
    std::cout << "Thread B: Attempting to lock mutex2..." << std::endl;
    mutex2.lock();
    std::this_thread::sleep_for(std::chrono::milliseconds(1));

    std::cout << "Thread B: Attempting to lock mutex1..." << std::endl;
    mutex1.lock();
    
    // 执行任务...
    
    mutex1.unlock();
    mutex2.unlock();
}

int main() {
    std::thread t1(threadA);
    std::thread t2(threadB);

    t1.join();
    t2.join();

    return 0;
}

可以看到程序一直卡住

为什么会卡住呢?

  1. 线程A首先尝试锁定mutex1,并成功获得锁。

  2. 线程B首先尝试锁定mutex2,并成功获得锁。

  3. 接下来,线程A想要锁定mutex2,但它被线程B持有,因此线程A被阻塞,无法继续执行,等待mutex2被释放。

  4. 同样地,线程B想要锁定mutex1,但它被线程A持有,因此线程B也被阻塞,等待mutex1被释放。

现在,线程A和线程B都被阻塞,它们相互等待对方释放资源,但又不会主动释放自己的锁。这就是典型的死锁情况:两个或多个线程互相等待对方释放资源,导致程序无法继续执行下去。

根本原因在于线程拿不到锁时就会被阻塞。

相关推荐
爱吃生蚝的于勒1 分钟前
深入学习指针(5)!!!!!!!!!!!!!!!
c语言·开发语言·数据结构·学习·计算机网络·算法
羊小猪~~5 分钟前
数据结构C语言描述2(图文结合)--有头单链表,无头单链表(两种方法),链表反转、有序链表构建、排序等操作,考研可看
c语言·数据结构·c++·考研·算法·链表·visual studio
binishuaio11 分钟前
Java 第11天 (git版本控制器基础用法)
java·开发语言·git
zz.YE13 分钟前
【Java SE】StringBuffer
java·开发语言
就是有点傻17 分钟前
WPF中的依赖属性
开发语言·wpf
洋24025 分钟前
C语言常用标准库函数
c语言·开发语言
进击的六角龙27 分钟前
Python中处理Excel的基本概念(如工作簿、工作表等)
开发语言·python·excel
wrx繁星点点28 分钟前
状态模式(State Pattern)详解
java·开发语言·ui·设计模式·状态模式
NoneCoder1 小时前
Java企业级开发系列(1)
java·开发语言·spring·团队开发·开发
苏三有春1 小时前
PyQt5实战——UTF-8编码器功能的实现(六)
开发语言·qt