【C++多线程编程:六种锁】

目录

普通互斥锁: 

轻量级锁

独占锁:

std::lock_guard:

std::unique_lock: 

共享锁:

超时的互斥锁

递归锁


普通互斥锁: 

std::mutex确保任意时刻只有一个线程可以访问共享资源,在多线程中常用于保护共享资源。

互斥锁的实现示例:

#include <iostream>
#include <thread>
#include <mutex>std::mutex Mutex;
int shared_source = 1;void Shared_data()
{//获取锁Mutex.lock();shared_source++;std::cout << "shared_source : " << shared_source <<" by thread :"<<std::this_thread::get_id()<< std::endl;//锁释放Mutex.unlock();
}
int main()
{std::thread thread1(Shared_data);std::thread thread2(Shared_data);thread1.join();thread2.join();return 0;
}

在实际使用的过程中,如果容易忘记对锁进行释放,可以使用std::unique_lock和std::lock_guard安全的管理锁的释放。 

轻量级锁

在C++中,轻量级锁通常指的是一些开销较小,性能较高的锁机制,C++并没有直接提供“轻量级锁”的概念,可以通过一个自旋锁来达到实现轻量级锁的目的。

适用场景:竞争不激烈,比如在大多数时间里中有一个线程需要访问临界区的情况。但如果竞争激烈会导致CUP资源的浪费(CPU自旋),降低性能。

自旋锁:一种简单的锁机制,当线程尝试获取锁时,如果锁已被其他线程占用,则当前线程会不断地循环等待,直到锁可用

自旋锁的示例:

#include <iostream>
#include <thread>
#include <atomic>std::atomic<bool>spinlock(false);void spin_lock()
{while (spinlock.exchange(true, std::memory_order_acquire)){//如果锁被占用则一直自旋,等待锁可用}
}void spin_unlock()
{spinlock.store(false, std::memory_order_release);//释放锁,并将锁的状态写入内存中
}
void critical_section()
{spin_lock();std::cout << "mutex acquire by thread : " << std::this_thread::get_id() << std::endl;
//	std::this_thread::sleep_for(std::chrono::seconds(1));spin_unlock();//锁释放
}
int main()
{std::thread t1(critical_section);std::thread t2(critical_section);t1.join();t2.join();return 0;
}

独占锁:

std::unique_lock和std::lock_guard都是cpp标准库提供的管理互斥锁的类,他们都提供了自动锁和解锁的功能。但二者存在一些关键区别:

std::lock_guard:

  • std::lock_guard并不是一种锁,而是一个作用域锁管理,一个能管理锁生命周期的工具,用于简化互斥锁的使用,确保在作用域结束后自动释放锁,避免死锁问题
  • 自动加锁和解锁:在构造时自动加锁,在析构时自动解锁,不需要显示的调用加锁和解锁的方法
  • 不支持条件变量配合使用:条件变量需要可以临时释放锁并重新获取锁,但是lock_guard并没有提供unlock().
  • 使用场景:适用于不需要显示控制加锁和解锁的场景,或者不需要与条件变量配合使用的场景,适用于在某个作用域中同步访问共享资源的场景

lock_guard示例:

#include <iostream>
#include <thread>
#include <mutex>std::mutex mtx;
int shared_data = 0;void worker()
{std::lock_guard<std::mutex>lock(mtx);//自动加锁shared_data++;std::cout << "shared_data ++ by thread : " << std::this_thread::get_id() << std::endl;//自动解锁,无需显示调用unlock()
}
int main()
{std::thread t1(worker);std::thread t2(worker);t1.join();t2.join();return 0;
}

std::unique_lock: 

  • std::unique_lock本身不是一种锁,而是一个可选的互斥锁管理器,提供了对互斥锁的更加灵活的控制方式
  • 显示加锁和解锁:提供了lock()和unlock()方法,可以显示的加锁和解锁,也可以在构造时自动加锁
  • 可以与条件变量配合
  • 自动管理锁的生命周期,避免了因忘记解锁而导致的死锁问题
  • 使用场景:需要显示控制加锁和解锁时机,或者需要和条件变量配合使用的场景

unique_lock实例:

#include <iostream>
#include <thread>
#include <mutex>std::mutex mtx;
int shared_data = 0;
char str = 'a';//不显示调用加解锁,自动加锁和解锁
void worker()
{std::unique_lock<std::mutex>lock(mtx);//defer_lock =>延迟锁定操作,需要在特定的时机才锁定互斥锁shared_data += 1;std::cout << "shared_data ++ :" << shared_data << " ,by thread : " << std::this_thread::get_id() << std::endl;std::cout << "str: " << str++ << " ,by thread: " << std::this_thread::get_id() << std::endl;}
//显示调用加锁和解锁
void worker()
{std::unique_lock<std::mutex>lock(mtx,std::defer_lock);//defer_lock =>延迟锁定操作,需要在特定的时机才锁定互斥锁shared_data+=1;std::cout << "shared_data ++ :"<<shared_data<<" ,by thread : " << std::this_thread::get_id() << std::endl;lock.lock();//延迟锁定std::cout << "str: " << str++ <<" ,by thread: "<< std::this_thread::get_id() << std::endl;lock.unlock();//显示解锁
}int main()
{std::thread t1(worker);std::thread t2(worker);t1.join();t2.join();return 0;
}

独占锁与mutex相比,更加的安全,它可以避免忘记手动解锁,会在其作用域结束时自动的释放锁  

共享锁:

在C++中,共享锁允许多个线程共享读取资源,但在写入资源时只允许一个线程写入数据,要求独占锁。当某个线程获取了独占锁时,其他线程无法获取任何形式地锁,而当多个线程获取共享锁时,他们可以同时读取共享资源

使用场景:适用于读多写少的场景下,能有效提高多线程程序的性能

示例:

#include <iostream>
#include <thread>
#include <shared_mutex>std::shared_mutex mtx;
int shared_data = 0;
void read_data()
{//获取共享锁std::shared_lock <std::shared_mutex> lock(mtx);std::cout << "Reading data : " << shared_data << std::endl;
}
void write_data()
{//获取独占锁std::unique_lock<std::shared_mutex>lock(mtx);shared_data = 24;std::cout << "Writing data : " << shared_data << std::endl;
}
int main()
{std::thread t1(read_data);std::thread t2(read_data);std::thread t3(write_data);t1.join();t2.join();t3.join();return 0;
}

超时的互斥锁

C++标准库提供了timed_mutex来实现该功能,支持超时机制,当线程尝试获取锁时,可以指定一个超时时间,如果在该时间内无法获取锁,线程将返回并继续执行其他任务。通过使用超时互斥锁,可以有效的避免线程在等待锁时无限地阻塞,提高程序地响应和稳定性 

示例: 

#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>//超时锁
//C++标准库提供的互斥锁之一,支持在尝试获取锁时设置超时时间
std::timed_mutex mtx;void function_time()
{//在1s内获取锁//try_lock_for在指定时间内获取锁,成功返回true,失败则false//参数chrono是一个时间间隔类型的一个域if (mtx.try_lock_for(std::chrono::seconds(1))){std::cout << "Lock acquired " << std::endl;//模拟耗时操作//在当前线程休眠这2s内,其他线程无法获取该锁//所以当t2尝试获取锁时,t1持有锁并休眠2s,当锁释放后,t2锁获取超时std::this_thread::sleep_for(std::chrono::seconds(2));mtx.unlock();//释放锁}else{std::cout << "Fail to acquire lock within 1s" << std::endl;}
}
int main()
{//主线程创建的这俩线程几乎同时开始执行function_time函数std::thread t1(function_time);std::thread t2(function_time);t1.join();t2.join();return 0;
}

递归锁

递归锁:同一个线程多次获取同一把锁,而不会导致死锁。cpp中没有直接提供递归锁的实现,但是可以通过reecursive_mutex来实现递归锁的功能。

reecursive_mutex:一个可重入的互斥锁,允许同一个线程多次调用lock或try_lock来获取锁,不会导致死锁。

使用场景:需要在同一个线程中多次获取锁的场景

递归锁示例:

#include <iostream>
#include <thread>
#include <mutex>std::recursive_mutex mtx;void function(int n)//递归
{if (n > 0){mtx.lock();std::cout << "lock acquired by thread : " << std::this_thread::get_id() << " n ; " << n << std::endl;function(n - 1);mtx.unlock();}}int main()
{std::thread t1(function,3);std::thread t2(function,2);t1.join();t2.join();return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/1375.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java项目之智慧农贸信息化管理平台(ssm+mybatis+mysql)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的码农一枚。今天要和大家聊的是一款基于ssm的智慧农贸信息化管理平台。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 智慧农贸信息化管理平台…

java人脸识别

文章目录 前言 为什么选择虹软呢&#xff1f; 注册虹软账号&#xff0c;下载SDK 将jar包安装到maven本地仓库 项目实战 导入jar包 编写配置文件 Service 编写测试类 人脸识别更多应用 前言 ‌虹软人脸识别技术‌是由虹软公司开发的一系列人脸识别技术&#xff0c;包括…

【学习路线】Python自动化运维 详细知识点学习路径(附学习资源)

学习本路线内容之前&#xff0c;请先学习Python的基础知识 其他路线&#xff1a; Python基础 >> Python进阶 >> Python爬虫 >> Python数据分析&#xff08;数据科学&#xff09; >> Python 算法&#xff08;人工智能&#xff09; >> Pyth…

Nginx代理同域名前后端分离项目的完整步骤

前后端分离项目&#xff0c;前后端共用一个域名。通过域名后的 url 前缀来区别前后端项目。 以 vue php 项目为例。直接上 server 模块的 nginx 配置。 server{ listen 80; #listen [::]:80 default_server ipv6onlyon; server_name demo.com;#二配置项目域名 index index.ht…

73.矩阵置零 python

矩阵置零 题目题目描述示例 1&#xff1a;示例 2&#xff1a;提示&#xff1a; 题解思路分析Python 实现代码代码解释提交结果 题目 题目描述 给定一个 m x n 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 示例…

【深度学习】通俗理解偏差(Bias)与方差(Variance)

在统计学习中&#xff0c;我们通常使用方差与偏差来衡量一个模型 1. 方差与偏差的概念 偏差(Bais)&#xff1a; 预测值和真实值之间的误差 方差(Variance)&#xff1a; 预测值之间的离散程度 低偏差低方差、高偏差低方差&#xff1a; 图中每个点表示同一个模型每次采样出不同…

Git学习记录

针对各个项目的gitignore文件示例 github/gitignore: A collection of useful .gitignore templates 忽略文件 文件 .gitignore 的格式规范如下&#xff1a; • 所有空行或者以 &#xff03; 开头的行都会被 Git 忽略。 • 可以使用标准的 glob 模式匹配。 • 匹配模式…

自然语言转 SQL:通过 One API 将 llama3 模型部署在 Bytebase SQL 编辑器

使用 Open AI 兼容的 API&#xff0c;可以在 Bytebase SQL 编辑器中使用自然语言查询数据库。 出于数据安全的考虑&#xff0c;私有部署大语言模型是一个较好的选择 – 本文选择功能强大的开源模型 llama3。 由于 OpenAI 默认阻止出站流量&#xff0c;为了简化网络配置&#…

Cookie和Session

会话&#xff1a; 有状态会话&#xff1a; 客户端知道发起请求的是谁 无状态会话&#xff1a; 不知道发起请求的是谁 只知道有请求 http是无状态请求 保存会话信息的两种技术&#xff1a; 可以通过Cookie和Session储存会话信息 cookie&#xff1a;客户端技术 信心存…

ImportError: attempted relative import with no known parent package 报错的解决!

本人在做调用超级鹰API解决点触验证码时&#xff0c;两次出现本报错。研究后解决&#xff0c;步骤如下&#xff1a;&#xff08;注意&#xff1a;如果项目目录结构简单且无中文目录&#xff0c;直接使用绝对路径即可解决&#xff01;&#xff01;&#xff01;&#xff09; 1.项…

介绍下不同语言的异常处理机制

Golang 在Go语言中&#xff0c;有两种用于处于异常的机制&#xff0c;分别是error和panic&#xff1b; panic panic 是 Go 中处理异常情况的机制&#xff0c;用于表示程序遇到了无法恢复的错误&#xff0c;需要终止执行。 使用场景 程序出现严重的不符合预期的问题&#x…

使用gtsam添加OrientedPlane3Factor平面约束因子

在基于地面约束的SLAM优化中&#xff0c;已知的地面信息&#xff08;如 plan.pcd 文件中的地面模型&#xff09;可以用作一个先验约束&#xff0c;以帮助优化位姿估计。具体而言&#xff0c;这个过程涉及将地面模型和每个帧的位姿结合&#xff0c;以创建一个因子模型&#xff0…

Cython全教程2 多种定义方式

—— 本篇文章&#xff0c;主要讲述Cython中的四种定义关键字 全教程2 多种定义方式&#xff1a; 在Cython中&#xff0c;关于定义的关键字有四个&#xff0c;分别是&#xff1a; cdef、def、cpdef、DEF 一、cdef定义关键字 顾名思义&#xff0c;cdef关键字定义的是一个C函数…

WINFORM - DevExpress -> DevExpress总结[安装、案例]

安装devexpress软件 路径尽量不换&#xff0c;后面破解不容易出问题 vs工具箱添加控件例如: ①使用控制台进入DevExpress安装目录: cd C:\Program Files (x86)\DevExpress 20.1\Components\Tools ②添加DevExpress控件&#xff1a; ToolboxCreator.exe/ini:toolboxcreator…

primitive 的 Appearance编写着色器材质

import { nextTick, onMounted, ref } from vue import * as Cesium from cesium import gsap from gsaponMounted(() > { ... })// 1、创建矩形几何体&#xff0c;Cesium.RectangleGeometry&#xff1a;几何体&#xff0c;Rectangle&#xff1a;矩形 let rectGeometry new…

《JavaWeb开发-javascript基础》

文章目录 《JavaWeb开发-javascript基础》1.javascript 引入方式2.JS-基础语法-书写语法2.1 书写语法2.2 输出语句 3.JS-基础语法-变量4.JS-基础语法-数据类型&运算符4.1 数据类型4.2 运算符4.3 数据类型转换 5. JS-函数6. JS-对象-Array数组7. JS-对象-String字符串8. JS-…

从CentOS到龙蜥:企业级Linux迁移实践记录(龙蜥开局)

引言&#xff1a; 在我们之前的文章中&#xff0c;我们详细探讨了从CentOS迁移到龙蜥操作系统的基本过程和考虑因素。今天&#xff0c;我们将继续这个系列&#xff0c;重点关注龙蜥系统的实际应用——特别是常用软件的安装和配置。 龙蜥操作系统&#xff08;OpenAnolis&#…

【python基础——异常BUG】

什么是异常(BUG) 检测到错误,py编译器无法继续执行,反而出现错误提示 如果遇到错误能继续执行,那么就捕获(try) 1.得到异常:try的执行,try内只可以捕获一个异常 2.预案执行:except后面的语句 3.传入异常:except … as uestcprint(uestc) 4.没有异常:else… 5.鉴定完毕,收尾的语…

MySQL的安装

MySQL典型的关系型数据库&#xff08;RDBMS&#xff09;&#xff1a;oracle、MySQL、SqlServer MySQL的版本 5.5~5.7、8.0 MySQL的安装和配置 下载地址&#xff1a; https://downloads.mysql.com/archives/community/ 安装包 (x86, 64-bit), MSI Installer 执行下一步即…

跨境电商领域云手机之选:亚矩阵云手机的卓越优势

在跨境电商蓬勃发展的当下&#xff0c;云手机已成为众多企业拓展海外市场的得力助手。亚矩阵云手机凭借其独特优势&#xff0c;在竞争激烈的云手机市场中崭露头角。不过&#xff0c;鉴于市场上云手机服务供应商繁多&#xff0c;企业在抉择时需对诸多要素予以审慎考量。 跨境电商…