在线程间共享数据---互斥量

在线程间共享数据—互斥量

std::mutex


std::mutex 是C++标准库中 <mutex> 头文件中定义的一个类,它是实现多线程同步的基础工具,主要用于保护共享资源,防止多个线程同时访问同一资源造成的数据竞争(race condition)。

std::mutex 表示一个互斥量(Mutex),它有两个主要的操作:

  1. 锁定(Locking):通过调用 lock() 函数来获取互斥锁。如果互斥量此时未被任何线程锁定,则当前线程获得该锁,否则线程将阻塞(即挂起),直到互斥量可用。

    std::mutex m;
    m.lock();
    // 在此处访问或修改受保护的资源
    m.unlock();
    
  2. 解锁(Unlocking):通过调用 unlock() 函数释放互斥锁,使得其他等待该锁的线程有机会获得锁并继续执行。

    m.unlock();
    

另外,std::mutex 还提供了诸如 try_lock()(尝试非阻塞地获取锁)和 lock_guardunique_lock 等辅助工具,帮助更安全、便捷地管理锁的生命周期。

std::lock_guard


std::lock_guard 是C++标准库 <mutex> 中的一个类,用于管理互斥锁(如 std::mutex)的生命周期,遵循RAII原则。它的主要目的是确保互斥锁在进入特定作用域时自动上锁,并在离开该作用域时自动解锁,从而有效地避免手动管理锁可能导致的问题,如忘记解锁、异常安全问题等。

使用 std::lock_guard 的典型方式如下:

#include <mutex>std::mutex mtx;void safe_operation() {std::lock_guard<std::mutex> guard(mtx); // 当guard创建时自动锁定mtx// 在此作用域内,互斥锁处于锁定状态,保证了对共享资源的独占访问// 对共享资源的访问和修改代码...
} // 当guard超出作用域被销毁时,会自动解锁mtx

在上述代码中,当 safe_operation 函数开始执行时,std::lock_guard 构造器会立即锁定 mtx 互斥量。然后,在 safe_operation 函数执行完毕(无论正常结束还是抛出异常)时,std::lock_guard 的析构函数会被自动调用,从而解锁互斥量。这样就确保了互斥锁始终会在适当的时候得到释放,提高了程序的健壮性和安全性。

std::unique_lock


std::unique_lock 是一个类,用于管理互斥量(如 std::mutexstd::recursive_mutex 等)的锁定和解锁操作。它遵循RAII(Resource Acquisition Is Initialization)原则,确保互斥量在适当的时间被锁定和解锁,即便在处理过程中遇到异常也能保证资源的安全性。

std::unique_lock 的特点和常见用法如下:

  1. 构造函数
    • 默认构造函数创建一个不关联任何互斥量的 std::unique_lock 对象。
    • 接收一个互斥量作为参数的构造函数会立即尝试锁定互斥量。
    • 通过传递一个额外的布尔标志 defer_locktry_to_lock 到构造函数,可以选择延迟锁定或尝试非阻塞锁定互斥量。
std::mutex mtx;
std::unique_lock<std::mutex> lock(mtx); // 直接锁定互斥量
std::unique_lock<std::mutex> deferred_lock(mtx, std::defer_lock); // 延迟锁定互斥量
std::unique_lock<std::mutex> try_lock(mtx, std::try_to_lock); // 尝试非阻塞锁定互斥量
  1. 方法

    • lock():锁定互斥量,如果互斥量已被锁定,则当前线程将阻塞,直到互斥量可用。
    • unlock():解锁互斥量。
    • try_lock():尝试非阻塞地锁定互斥量,若成功则返回 true,否则返回 false
    • owns_lock():检查 std::unique_lock 是否持有互斥锁。
  2. RAII 特性

    • std::unique_lock 对象离开作用域时,如果仍持有互斥锁,则会自动释放(解锁)互斥量,从而避免了手动解锁带来的风险。
  3. 迁移所有权

    • std::unique_lock 允许你转移对互斥量的锁定所有权,这意味着你可以将一个 std::unique_lock 对象的所有权转移到另一个对象,而原对象将不再拥有互斥锁。

举例:

std::mutex mtx;
{std::unique_lock<std::mutex> lock(mtx);// 在此作用域内,互斥量被锁定// 对共享资源的访问和修改...
} // lock离开作用域时,互斥量自动解锁// 或者使用try_lock和unlock
std::unique_lock<std::mutex> ul(mtx, std::try_to_lock);
if (ul.owns_lock()) {// 已经成功锁定互斥量,执行操作...
} else {// 未能锁定互斥量,执行其他操作...
}
ul.unlock(); // 手动解锁

总的来说,std::unique_lock 提供了一种安全、便捷的方式来管理互斥量的锁定和解锁,尤其适合用于那些需要保证资源安全访问的多线程环境。

避免死锁–std::lock


std::lock 是一个函数,用于同时锁定多个互斥量(如 std::mutexstd::recursive_mutex 等),并且保证所有互斥量都是以一种无死锁的方式进行锁定的。这对于需要同时保护多个资源的场景非常有用。
std::lock 函数在成功返回后,它尝试锁定的所有互斥量都将保持锁定状态。

使用 std::lock 的一般形式如下:

#include <mutex>
#include <vector>std::mutex mtx1, mtx2, mtx3; // 假设有多个需要同时锁定的互斥量void safe_multiple_access() {std::vector<std::unique_lock<std::mutex>> locks;locks.reserve(3); // 预先分配足够的空间// 使用std::lock同时锁定多个互斥量std::lock(mtx1, mtx2, mtx3);// 将锁定的互斥量放入unique_lock容器中以便后续解锁locks.emplace_back(mtx1, std::adopt_lock);locks.emplace_back(mtx2, std::adopt_lock);locks.emplace_back(mtx3, std::adopt_lock);// 在此作用域内,所有互斥量都已锁定,可以安全地访问相关资源// ...} // 退出作用域后,unique_lock对象会自动解锁互斥量

在上述代码中,std::lock 函数接受一个或多个互斥量作为参数,并尽可能按照最优化的顺序尝试锁定它们,如果无法同时锁定所有互斥量,函数将会一直阻塞直到可以成功锁定为止。这种方法可以避免死锁的发生,因为所有互斥量都会以一种一致的顺序尝试锁定。

需要注意的是,std::lock 并不会直接返回锁住的互斥量,因此通常配合 std::unique_lock 或者其他的 RAII机制来确保在适当的时候解锁互斥量。在上面的例子中,我们使用了 std::unique_lock 并设置了 std::adopt_lock 标志来接手已经由 std::lock 锁定的互斥量。当这些 std::unique_lock 对象离开作用域时,它们会自动解锁对应的互斥量。

单次初始化–std::once_flag

std::once_flag 是C++标准库 <mutex> 中的一个类,用于实现单次初始化(也称为一次初始化或惰性初始化)的线程安全机制。在多线程环境下,有时我们希望某些初始化操作只执行一次,不论有多少线程试图执行它。std::once_flagstd::call_once 函数配合使用,可以确保某个特定的初始化代码块在整个程序生命周期中仅被执行一次。

基本使用方式如下:

#include <mutex>std::once_flag init_flag; // 创建一个once_flag实例// 初始化函数
void initialize() {// 这里是初始化逻辑,只会被执行一次// ...
}// 在多线程环境中安全调用initialize函数
void thread_safe_init() {std::call_once(init_flag, initialize); // 如果尚未初始化,则执行initialize函数
}int main() {std::thread t1(thread_safe_init);std::thread t2(thread_safe_init);t1.join();t2.join();// 即使这里有更多线程调用 thread_safe_init,initialize 函数也只会执行一次return 0;
}

在这个例子中,std::once_flag 实例 init_flagstd::call_once 函数搭配,确保了 initialize 函数在整个程序运行期间只执行一次,不管有多少线程调用了 thread_safe_init 函数。这样就能在多线程环境下安全地进行单次初始化操作。

读写锁


  • std::shared_mutex

std::shared_mutex 是 C++14 引入的标准库中的一个类,用于实现读写锁(Read-Write Lock)的功能。读写锁允许在多线程环境中更高效地管理对共享资源的访问:

  • 当多个线程希望读取共享资源时,它们可以同时获取读取锁(shared lock),也就是多个读线程可以并发访问资源。
  • 当一个线程希望修改(写入)共享资源时,它必须获取写入锁(exclusive lock)。在写入锁被持有的情况下,任何其他试图获取读取锁或写入锁的线程都会被阻塞,直到写入锁被释放。

使用 std::shared_mutex 可以确保在任何时刻,最多只有一个线程持有写入锁,但可以有任意数量的线程持有读取锁。这样既能保障数据的一致性,又能充分利用多核处理器的优势,在读取密集型操作中提升并发性能。

  • std::shared_lock

std::shared_lock 是C++标准库 <shared_mutex> 中的一个类,用于管理读写锁(std::shared_mutex 或兼容的类型)的共享锁部分。共享锁允许多个线程同时读取(共享访问)受保护的资源,但不允许任何线程在同一时间写入(独占访问)资源。

使用 std::shared_lock 的好处在于它可以确保在多线程环境下,多个读取线程可以同时访问共享资源,而不需要互相等待。当线程不再需要读取资源时,std::shared_lock 会在析构时自动释放共享锁。

#include <shared_mutex>
#include <iostream>std::shared_mutex mtx;
int shared_data = 0;void reader() {std::shared_lock<std::shared_mutex> read_lock(mtx);// 在此作用域内,可以安全地读取 shared_datastd::cout << "Reader: " << shared_data << '\n';
}void writer() {std::unique_lock<std::shared_mutex> write_lock(mtx);// 在此作用域内,可以安全地修改 shared_data++shared_data;std::cout << "Writer: Updated to " << shared_data << '\n';
}int main() {std::thread reader_thread1(reader);std::thread reader_thread2(reader);// 确保读者线程已经开始读取std::this_thread::sleep_for(std::chrono::milliseconds(100));std::thread writer_thread(writer);reader_thread1.join();reader_thread2.join();writer_thread.join();return 0;
}

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

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

相关文章

Ubuntu安装配置FTP

1、安装服务端 sudo apt-get install vsftpd service vsftpd start service vsftpd stop service vsftpd restart 2、创建目录、用户 mkdir -p /home/xxxx sudo useradd -g ftp -d /home/vsftp -m angler 命令一&#xff1a;这种命令会在登录界面显示用户名 sudo …

vue3大事件项目3

弹框验证 先准备变量: const formModel ref({ cate_name: , cate_alias: }) 还有规则&#xff1a; const rules { cate_name: [ { required: true, message: please input name, trigger: blur }, { pattern: /^\S{1,10}$/, message: must be 1-10, trigger: blur } ], …

Java 基于微信小程序的智能停车场管理小程序

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

Rust腐蚀服务器常用参数设定详解

Rust腐蚀服务器常用参数设定详解 大家好我是艾西&#xff0c;一个做服务器租用的网络架构师上期我们分享了rust腐蚀服务器的windows系统搭建方式&#xff0c;其中启动服务器bat参数因为涉及的东西比较多所以想通过这篇文章给大家做一下详细的分享。 &#xff08;注本文中xxxx…

浅述.Net中的Hash算法(顺带对称、非对称算法)

【写在前面】 对称加密算法(只有一个私钥&#xff0c;比如DES【不推荐】、AES)&#xff1b; 非对称加密算法&#xff08;公钥与私钥&#xff0c;比如RSA&#xff09;&#xff1b; Hash算法也称为散列函数算法&#xff0c;任意长度的数据都转换为固定长度的字符串&#xff08…

【自然语言处理八-transformer实现翻译任务-一(输入)】

自然语言处理八-transformer实现翻译任务-一&#xff08;输入&#xff09; transformer架构数据处理部分模型的输入数据(图中inputs outputs outputs_probilities对应的label)以处理英中翻译数据集为例的代码 positional encoding 位置嵌入代码 鉴于transfomer的重要性&#xf…

小猫爬山(c++题解)

题目描述 Freda和rainbow饲养了N只小猫&#xff0c;这天&#xff0c;小猫们要去爬山。经历了千辛万苦&#xff0c;小猫们终于爬上了山顶&#xff0c;但是疲倦的它们再也不想徒步走下山了&#xff08;呜咕>_<&#xff09;。 Freda和rainbow只好花钱让它们坐索道下山。索…

1.汉诺塔问题

C力扣 汉诺塔 class Solution { public:void hanota(vector<int>& a, vector<int>& b, vector<int>& c) {dfs(a,b,c,a.size());}void dfs(vector<int>& a, vector<int>& b, vector<int>& c,int n){if(n1){c.push…

阻塞队列和生产消费模型

阻塞队列 阻塞队列的概念 队列相信我们已经不陌生了 之前也学过很多队列 比如: 普通队列 和 优先级队列 两种 这两种队列都是线程不安全的 而我们讲的阻塞队列 刚好可以解决线程安全问题 也是先进先出 并且带有阻塞功能. 阻塞功能是怎么回事呢 就是如果入队的时候阻塞队列为…

rabbitmq安装rabbitmq-delayed-message-exchange插件

下载地址&#xff1a;Community Plugins | RabbitMQ 上传到rabbitmq安装目录的/plugins目录下 我的是/usr/lcoal/rabbitmq/plugins/ 直接安装 [rootk8s-node1 rabbitmq]# rabbitmq-plugins enable rabbitmq_delayed_message_exchange [rootk8s-node1 rabbitmq]# rabbitmq-pl…

pringboot2集成swagger2出现guava的FluentIterable方法不存在

错误信息 Description: An attempt was made to call a method that does not exist. The attempt was made from the following location: springfox.documentation.spring.web.scanners.ApiListingScanner.scan(ApiListingScanner.java:117) The following method did not ex…

《青少年成长管理2024》049 “成长目标:到哪儿了?”2/2

《青少年成长管理2024》049 “成长目标&#xff1a;到哪儿了&#xff1f;”2/2 六、规则和契约七、资源与欲望八、愚蠢与智慧九、奋勇争第一十、时代新节奏 本节摘要&#xff1a;当你来到这个世界&#xff0c;首先认识一下这是一个什么样的世界&#xff0c;你处于一个什么样的环…

c语言例题,求数组中最大值,99乘法口诀表

例题1&#xff1a;求出数组中最大的值 根据题意&#xff0c;我们知道的是需要从一个数组中找到一个最大的元素并且输出。那首先我们先建立一个数组&#xff0c;然后将一些不有序的整型元素放到数组中&#xff0c;然后再建立一个变量来存放数组中的第一个元素&#xff0c;通过一…

Linux学习 - 又双叒叕一个软件安装方法

Linux学习 - 又双叒叕一个软件安装方法 Conda包管理系统 Conda是一种通用包管理系统&#xff0c;旨在构建和管理任何语言的任何类型的软件。通常与Anaconda (集成了更多软件包&#xff0c;https://www.anaconda.com/download/#download)和Miniconda(只包含基本功能软件包, ht…

算法设计与分析实验报告c++实现(八皇后问题、连续邮资问题、卫兵布置问题、圆排列问题)

一、实验目的 1&#xff0e;加深学生对回溯法算法设计方法的基本思想、基本步骤、基本方法的理解与掌握&#xff1b; 2&#xff0e;提高学生利用课堂所学知识解决实际问题的能力&#xff1b; 3&#xff0e;提高学生综合应用所学知识解决实际问题的能力。 二、实验任务 用回溯…

vue简单使用五(组件的使用)

目录 如何定义组件&#xff1a; 组件的命名&#xff1a; 父组件向子组件传值&#xff1a; 子组件向父组件传值&#xff1a; 如何定义组件&#xff1a; 全局组件定义&#xff1a; 局部组件定义&#xff1a; 组件的基本使用&#xff1a; 打印结果&#xff1a; 组件的命名&#xf…

蓝桥杯真题有奖问答

小蓝正在参与—个现场问答的节目。活动中一共有30道题目,每题只有答对和答错两种情况,每答对—题得10分&#xff0c;答错—题分数归零。小蓝可以在任意时刻结束答题并获得目前分数对应的奖项&#xff0c;之后不能再答任何题目。最高奖项需要100分,所以到达100分时小蓝会直接停止…

分类预测 | Matlab实现OOA-BP鱼鹰算法优化BP神经网络数据分类预测

分类预测 | Matlab实现OOA-BP鱼鹰算法优化BP神经网络数据分类预测 目录 分类预测 | Matlab实现OOA-BP鱼鹰算法优化BP神经网络数据分类预测分类效果基本介绍程序设计参考资料 分类效果 基本介绍 1.Matlab实现OOA-BP鱼鹰算法优化BP神经网络多特征分类预测&#xff08;完整源码和数…

MATLAB Simulink仿真搭建及代码生成技术—01自定义新建模型模板

MATLAB Simulink仿真搭建及代码生成技术 目录 01-自定义新建模型模板点击运行&#xff1a;显示效果&#xff1a;查看模型设置&#xff1a; 01-自定义新建模型模板 新建模型代码如下&#xff1a; function new_model(modelname) %建立一个名为SmartAss的新的模型并打开 open_…

【微服务】------常见模型的分析与比较

DDD 分层架构 整洁架构 整洁架构又名“洋葱架构”。为什么叫它洋葱架构&#xff1f;看看下面这张图你就明白了。整洁架构的层就像洋葱片一样&#xff0c;它体现了分层的设计思想。 整洁架构最主要的原则是依赖原则&#xff0c;它定义了各层的依赖关系&#xff0c;越往里依赖越…