【C++】详解std::mutex

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

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


目录

  • 概述
  • 头文件
  • std::mutex类的成员
  • 类型
  • 方法
  • 没有std::mutex会产生什么问题
  • 问题一:数据竞争
  • 问题二:不一致
  • lock和unlock
  • 死锁

概述

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

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

头文件

#include

std::mutex类的成员

类型

native_handle_type    原生的互斥锁句柄类型

方法

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

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

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

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

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

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

问题一:数据竞争

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

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

#include 
#include 
#include 

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值
}

【C++】详解std::mutex_第1张图片

问题二:不一致

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

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

#include 
#include 
#include 

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();

}

【C++】详解std::mutex_第2张图片

lock和unlock

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

#include 
#include 
#include 

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; 
}

【C++】详解std::mutex_第3张图片

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

死锁

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

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

#include 
#include 
#include 

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;
}

【C++】详解std::mutex_第4张图片

可以看到程序一直卡住

为什么会卡住呢?

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

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

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

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

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

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

你可能感兴趣的:(#,C++多线程,c++,开发语言)