【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都被阻塞,它们相互等待对方释放资源,但又不会主动释放自己的锁。这就是典型的死锁情况:两个或多个线程互相等待对方释放资源,导致程序无法继续执行下去。

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

相关推荐
VBA63371 小时前
VBA之Word应用第四章第五节:段落Paragraph对象的属性(一)
开发语言
whltaoin1 小时前
【Java SE】Java IO体系深度剖析:从原理到实战的全方位讲解(包含流操作、序列化与 NIO 优化技巧)
java·开发语言·nio·se·io体系
csbysj20205 小时前
jQuery 删除元素
开发语言
xxy-mm5 小时前
Javascript 中的继承
开发语言·javascript·ecmascript
quikai19817 小时前
python练习第二组
开发语言·python
AI视觉网奇7 小时前
Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr
开发语言·c++·算法
智者知已应修善业7 小时前
【输入两个数字,判断两数相乘是否等于各自逆序数相乘】2023-10-24
c语言·c++·经验分享·笔记·算法·1024程序员节
wjs20247 小时前
并查集快速合并
开发语言
free-elcmacom7 小时前
MATLAB与高等数学<1>一道曲面积分题的几何直观
开发语言·数学建模·matlab·高等数学
LaoZhangGong1237 小时前
深度学习uip中的“psock.c和psock.h”
c语言·开发语言