【C++】详解std::mutex

2023-09-11 23:26:13

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,但是事实上却不是这样,这就是数据竞争所造成的。

#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

#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可以保证任何时刻只有一个线程在访问共享资源,从而避免数据竞争问题。

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

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

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

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

更多推荐

Openresty(二十一)ngx.balance和balance_by_lua灰度发布

一openresty实现灰度发布①灰度发布说明:'早期'博客对'灰度'发布的'概念'进行解读,并且对'原生nginx'灰度实现进行讲解后续:主要拿'节点引流'的灰度发布,并且关注'gray灰度策略'相关借鉴②回顾HTTP反向代理流程ngx_http_upstream可'操作'点:根据'负载均衡策略'选择上游的服务器wr

【AI】机器学习——支持向量机(线性模型)

支持向量机是一种二分类算法,通过在高维空间中构建超平面实现对样本的分类文章目录5.1SVM概述5.1.1分类5.2线性可分SVM5.2.1线性可分SVM基本思想5.2.2策略函数间隔几何间隔硬间隔最大化5.2.3原始算法支持向量5.2.4对偶形式算法1.构造并求解对偶问题2.计算参数3.求得分离超平面优点例题5.支持向

基于微信小程序的自习室系统设计与实现,可作为毕业设计

博主介绍:✌程序员徐师兄、7年大厂程序员经历。全网粉丝30W+、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌文章目录1简介2技术栈3需求分析3.1用户需求分析3.1.1学生用户3.1.3管理员用户4数据库设计4.4.1ER图设计4.4.2数据库表设计**第五章

利用 Python PyPDF2库轻松提取PDF文本(及其他高级操作)

当需要从PDF文件中提取文本时,Python中的PyPDF2库是一个非常有用的工具。无论您是需要分析PDF文档中的内容还是需要在文档中搜索特定的信息,PyPDF2都可以帮助您轻松实现这些任务。在本文中,我们将探讨如何使用PyPDF2库提取PDF文件中的文本,并提供一些示例代码来帮助您入门。安装PyPDF2库首先,您需要

手摸手系列之前端Vue实现PDF预览及打印的终极解决方案

前言近期我正在开发一个前后端分离项目,使用了SpringBoot和Vue2,借助了国内优秀的框架jeecg,前端UI库则选择了ant-design-vue。在项目中,需要实现文件上传功能,同时还要能够在线预览和下载图片和PDF文件,甚至需要在页面上直接打印PDF文件。尽管框架自带了vue-print-nb-jeecg组

AI引擎助力,CamScanner智能高清滤镜开启扫描新纪元!

文章目录⭐写在前面⭐突破图像处理难点:扫描全能王的独特优势⭐耳听为虚,眼见为实⭐产品背后的主要核心:AI-Scan助力⭐深度学习助力智能文档处理的国际化进程⭐品味智能文档处理的轻松与精准⭐写在前面在数字化快速发展的今天,我们时常会遇到需要将纸质文件转变为电子文字的场景。无论是工作中的合同、报告,还是日常生活中的笔记、名

开源与人工智能:现状与展望

🌷🍁博主猫头虎带您GotoNewWorld.✨🍁🦄博客首页——猫头虎的博客🎐🐳《面试题大全专栏》文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺🌊《IDEA开发秘籍专栏》学会IDEA常用操作,工作效率翻倍~💐🌊《100天精通Golang(基础入门篇)》学会Golang语言,畅玩云原生,走遍大

笔记:Android 应用启动流程

1.点击图标,启动app点击图标,实际是封装了一个Intent然后调用了startActivity方法ComponentNamecomponentName=newComponentName("包名","activity名称");Intentintent=newIntent(Intent.ACTION_MAIN);int

如何玩转CSDN AI工具集

前言人工智能生成内容(AIGC)是当下最具有前景的技术领域之一。AI能够以惊人的速度和准确度生成各种类型的内容,完成文章翻译、代码生成、AI对话、插图创作等工作,带来了许多令人兴奋的机遇。本文将介绍CSDNAI工具集的基本使用方法,现在您可以免费试用全部功能。一、AI工具集AI工具集是CSDN的一款综合AI赋能平台,集

Java 基于 SPringBoot 的幼儿园管理系统,附源码、数据库

博主介绍:✌程序员徐师兄、7年大厂程序员经历。全网粉丝30W+,Csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌文章目录一、效果演示二、前言介绍三、主要技术四、系统设计(部分)4.1、主要功能模块设计4.2、系统登录设计五、运行截图5.1、用户功能模块5.1.1、

[GIT]版本控制工具

[GIT]版本控制工具Git的命令Git的配置信息查看现有Git配置信息设置Git配置信息用户信息配置文本编辑器配置差异分析工具配置编辑Git配置文件Git仓库操作初始化Git仓库克隆Git仓库Git分支仓库创建Git远程仓库命令Git提交历史Git标签添加标签查看已有标签删除标签查看标签版本的修改内容其它操作Git是

热文推荐