C++ 互斥
mutex
mutex 类是能用于保护共享数据免受从多个线程同时访问的同步原语。
mutex 提供排他性非递归所有权语义:
- 调用方线程从它成功调用 lock 或 try_lock 开始,到它调用 unlock 为止占有 mutex 。
- 线程占有 mutex 时,所有其他线程若试图要求 mutex 的所有权,则将阻塞(对于 lock 的调用)或收到 false 返回值(对于 try_lock )。
- 调用方线程在调用 lock 或 try_lock 前必须不占有 mutex 。
- 若 mutex 在仍为任何线程所占有时即被销毁,或在占有 mutex 时线程终止,则行为未定义。
- mutex 类满足互斥体 (Mutex) 和标准布局类型 (StandardLayoutType) 的全部要求。
std::mutex 既不可复制亦不可移动。
成员类型
native_handle_type(可选) 实现定义
成员函数
(构造函数) 构造互斥(公开成员函数)
(析构函数) 销毁互斥(公开成员函数)
operator=[被删除] 不可复制赋值(公开成员函数)
锁定
lock 锁定互斥,若互斥不可用则阻塞(公开成员函数)
try_lock 尝试锁定互斥,若互斥不可用则返回(公开成员函数)
unlock 解锁互斥(公开成员函数)
原生句柄
native_handle 返回底层实现定义的原生句柄(公开成员函数)
注意:通常不直接使用 std::mutex :std::unique_lock 、 std::lock_guard 或 std::scoped_lock (C++17 起)以更加异常安全的方式管理锁定。
recursive_mutex
recursive_mutex 类是同步原语,能用于保护共享数据免受从个多线程同时访问。
recursive_mutex 提供排他性递归所有权语义:
- 调用方线程在从它成功调用 lock 或 try_lock 开始的时期里占有 recursive_mutex 。此时期间,线程可以进行对 lock 或 try_lock 的附加调用。所有权的时期在线程调用 unlock 匹配次数时结束。
- 线程占有 recursive_mutex 时,若其他所有线程试图要求 recursive_mutex 的所有权,则它们将阻塞(对于调用 lock )或收到 false 返回值(对于调用 try_lock )。
- 可锁定 recursive_mutex 次数的最大值是未指定的,但抵达该数后,对 lock 的调用将抛出std::system_error 而对 try_lock 的调用将返回 false 。
若 recursive_mutex 在仍为某线程占有时被销毁,则程序行为未定义。 recursive_mutex 类满足互
斥体 (Mutex) 和标准布局类型 (StandardLayoutType) 的所有要求。
成员类型
native_handle_type(可选) 实现定义
成员函数
(构造函数) 构造互斥(公开成员函数)
(析构函数) 销毁互斥(公开成员函数)
operator=[被删除] 不可复制赋值(公开成员函数)
锁定
lock 锁定互斥,若互斥不可用则阻塞(公开成员函数)
try_lock 尝试锁定互斥,若互斥不可用则返回(公开成员函数)
unlock 解锁互斥(公开成员函数)
原生句柄
native_handle 返回底层实现定义的原生句柄(公开成员函数)
//recursive_mutex 的使用场景之一是保护类中的共享状态,而类的成员函数可能相互调用
运行此代码
#include <iostream>
#include <thread>
#include <mutex>
class X {
std::recursive_mutex m;
std::string shared;
public:
void fun1() {
std::lock_guard<std::recursive_mutex> lk(m);
shared = "fun1";
std::cout << "in fun1, shared variable is now " << shared << '\n';
}
void fun2() {
std::lock_guard<std::recursive_mutex> lk(m);
shared = "fun2";
std::cout << "in fun2, shared variable is now " << shared << '\n';
fun1(); // 递归锁在此处变得有用
std::cout << "back in fun2, shared variable is " << shared << '\n';
};
};
int main()
{
X x;
std::thread t1(&X::fun1, &x);
std::thread t2(&X::fun2, &x);
t1.join();
t2.join();
}
可能的输出:
in fun1, shared variable is now fun1
in fun2, shared variable is now fun2
in fun1, shared variable is now fun1
back in fun2, shared variable is fun1
shared_mutex
shared_mutex 类是一个同步原语,可用于保护共享数据不被多个线程同时访问。与便于独占访问的其他互斥类型不同,shared_mutex 拥有二个访问级别:
- 共享 - 多个线程能共享同一互斥的所有权。
- 独占性 - 仅一个线程能占有互斥。
- 若一个线程已获取独占性锁(通过 lock 、 try_lock ),则无其他线程能获取该锁(包括共享的)。
仅当任何线程均未获取独占性锁时,共享锁能被多个线程获取(通过 lock_shared 、 try_lock_shared )。
在一个线程内,同一时刻只能获取一个锁(共享或独占性)。
共享互斥体在能由任何数量的线程同时读共享数据,但一个线程只能在无其他线程同时读写时写同一数据时特别有用。
shared_mutex 类满足共享互斥体 (SharedMutex) 和标准布局类型 (StandardLayoutType) 的所有要求。
成员类型
native_handle_type(可选) 实现定义
成员函数
(构造函数) 构造互斥(公开成员函数)
(析构函数) 销毁互斥(公开成员函数)
operator=[被删除] 不可复制赋值(公开成员函数)
排他性锁定
lock 锁定互斥,若互斥不可用则阻塞(公开成员函数)
try_lock 尝试锁定互斥,若互斥不可用则返回(公开成员函数)
unlock 解锁互斥(公开成员函数)
共享锁定
lock_shared 为共享所有权锁定互斥,若互斥不可用则阻塞(公开成员函数)
try_lock_shared 尝试为共享所有权锁定互斥,若互斥不可用则返回(公开成员函数)
unlock_shared 解锁互斥(共享所有权)(公开成员函数)
原生句柄
native_handle 返回底层实现定义的原生句柄(公开成员函数)
#include <iostream>
#include <mutex> // 对于 std::unique_lock
#include <shared_mutex>
#include <thread>
class ThreadSafeCounter {
public:
ThreadSafeCounter() = default;
// 多个线程/读者能同时读计数器的值。
unsigned int get() const {
std::shared_lock<std::shared_mutex> lock(mutex_);
return value_;
}
// 只有一个线程/写者能增加/写线程的值。
void increment() {
std::unique_lock<std::shared_mutex> lock(mutex_);
value_++;
}
// 只有一个线程/写者能重置/写线程的值。
void reset() {
std::unique_lock<std::shared_mutex> lock(mutex_);
value_ = 0;
}
private:
mutable std::shared_mutex mutex_;
unsigned int value_ = 0;
};
int main() {
ThreadSafeCounter counter;
auto increment_and_print = [&counter]() {
for (int i = 0; i < 3; i++) {
counter.increment();
std::cout << std::this_thread::get_id() << ' ' << counter.get() << '\n';
// 注意:写入 std::cout 实际上也要由另一互斥同步。省略它以保持示例简洁。
}
};
std::thread thread1(increment_and_print);
std::thread thread2(increment_and_print);
thread1.join();
thread2.join();
}
// 解释:下列输出在单核机器上生成。 thread1 开始时,它首次进入循环并调用 increment() ,
// 随后调用 get() 。然而,在它能打印返回值到 std::cout 前,调度器将 thread1 置于休眠
// 并唤醒 thread2 ,它显然有足够时间一次运行全部三个循环迭代。再回到 thread1 ,它仍在首个
// 循环迭代中,它最终打印其局部的计数器副本的值,即 1 到 std::cout ,再运行剩下二个循环。
// 多核机器上,没有线程被置于休眠,且输出更可能为递增顺序。
可能的输出:
单核:
123084176803584 2
123084176803584 3
123084176803584 4
123084185655040 1
123084185655040 5
123084185655040 6
多核:
140623314495232 2
140623314495232 3
140623314495232 4
140623306102528 4
140623306102528 5
140623306102528 6
C++ 互斥的更多相关文章
- ucos实时操作系统学习笔记——任务间通信(互斥锁)
想讲一下ucos任务间通信中的mutex,感觉其设计挺巧妙,同sem一样使用的是event机制实现的,代码不每一行都分析,因为讲的没邵贝贝老师清楚,主要讲一下mutex的内核是如何实现的.可以理解互斥 ...
- 多线程之互斥锁(By C++)
首先贴一段win32API实现的多线程的代码,使用CreateThread实现,如果不要传参数,就把第四个参数设为NULL #include<Windows.h> #include< ...
- Java基础加强之多线程篇(线程创建与终止、互斥、通信、本地变量)
线程创建与终止 线程创建 Thread类与Runnable接口的关系 public interface Runnable { public abstract void run(); } public ...
- 简单的JavaScript互斥锁
去年有几个项目需要使用JavaScript互斥锁,所以写了几个类似的,这是其中一个: //Published by Indream Luo //Contact: indreamluo@qq.com / ...
- C#互斥体——Mutex
Mutex对象是一个同步基元,可以用来做线程间的同步. 若多个线程需要共享一个资源,可以在这些线程中使用Mutex同步基元.当某一个线程占用Mutex对象时,其他也需要占用Mutex的线程将处于挂起状 ...
- Java多线程系列--“JUC锁”02之 互斥锁ReentrantLock
本章对ReentrantLock包进行基本介绍,这一章主要对ReentrantLock进行概括性的介绍,内容包括:ReentrantLock介绍ReentrantLock函数列表ReentrantLo ...
- 异步与并行~ReaderWriterLockSlim实现的共享锁和互斥锁
返回目录 在System.Threading.Tasks命名空间下,使用ReaderWriterLockSlim对象来实现多线程并发时的锁管理,它比lock来说,性能更好,也并合理,我们都知道lock ...
- 嵌入式Linux驱动学习之路(十四)按键驱动-同步、互斥、阻塞
目的:同一个时刻,只能有一个应用程序打开我们的驱动程序. ①原子操作: v = ATOMIC_INIT( i ) 定义原子变量v并初始化为i atomic_read(v) 返回原子变量 ...
- 进程互斥和fork
自父进程继承 进程的资格(真实(real)/有效(effective)/已保存(saved) 用户号(UIDs)和组号(GIDs)) 环境(environment) 堆栈 内存 打开文件的描述符(注意 ...
- Delphi多线程编程--线程同步的方法(事件、互斥、信号、计时器)简介
更详细的可以参考:http://www.cnblogs.com/xumenger/p/4450659.html 或者参考之后的博客 四个系统内核对象(事件.互斥.信号.计时器)都是线程同步的手段,从这 ...
随机推荐
- vs 工程中替换 Qt 静态库
上篇介绍了如何编译 Qt 静态库 编译 windows 上的 qt 静态库 这篇介绍如何替换已有的 Qt 静态库,比如 Qt5.15.0 有很多 bug,我们不得不提升 Qt 版本来避免 bug 导致 ...
- Golang从入门到跑路-从基础到微服务学习路线图
收录的awesome-go项目,学习基础系列,go项目实战,go源码分析,go开发者成长路线图等等,把他们收集起来学习. 地址:https://github.com/jiujuan/go-collec ...
- Flink本地模式安装和使用
Flink官方主页:https://flink.apache.org/ . 安装部署 不同版本的Flink对JDK版本要求不尽相同,需要根据具体的Flink版本要求先安装好JDK环境. 通常,在Fli ...
- NSIS制作安装包笔记(二):NSIS使用NSIS+Qt界面制作安装包流程
前言 Nsis可以使用duilib也可以使用qt界面,笔者主要qt,本文章梳理nsis+qt制作安装包的基本流程. 下载Nsis-Ui-Plugin插件 Github地址:https:// ...
- 第123篇: JS函数属性与方法
好家伙,本篇为<JS高级程序设计>第十章"函数"学习笔记 ECMAScript 中的函数是对象,因此有属性和方法. 1.函数属性 每个函数都有两个属性:length 和 ...
- C++ 深拷贝浅拷贝
C++ 深拷贝浅拷贝 C++默认生成的拷贝构造函数,他的行为就是浅拷贝,他只会复制一个一模一样的的指针,并不会操作指针指向的东西. 要想实现我们的逻辑需求,就要自定义拷贝构造函数,实现深拷贝. 我们来 ...
- Java面试挂在线程创建后续,不要再被八股文误导了!创建线程的方式只有1种
写在开头 在上篇博文中我们提到小伙伴去面试,面试官让说出8种线程创建的方式,而他只说出了4种,导致面试挂掉,在博文中也给出了10种线程创建的方式,但在文章的结尾我们提出:真正创建线程的方式只有1种,剩 ...
- 小工具 --- 百度翻译API翻译工具
引言 最近想把一些英文官方文档的资料翻译成中文,然后转化为Markdown文档,然后发现百度通用翻译的API有不错的免费额度,个人申请也能申请到高级版.这个额度足够个人的日常使用了. 如何使用 如何使 ...
- 2024-03-09:用go语言,我们把无限数量的栈排成一行,按从左到右的次序从 0 开始编号, 每个栈的的最大容量 capacity 都相同。实现一个叫「餐盘」的类 DinnerPlates, Di
2024-03-09:用go语言,我们把无限数量的栈排成一行,按从左到右的次序从 0 开始编号, 每个栈的的最大容量 capacity 都相同.实现一个叫「餐盘」的类 DinnerPlates, Di ...
- 选择单词后 按 ctrl + space 单词发音
需求: 在ide或其他地方,经常有单词发音不是很确定,但并不要很详细 就听个单词发音. 确定快捷键: 左手单手操作,我键盘上貌似就 左边的ctrl和空格键 还没有设定 翻译软件: 使用 pc端的 欧路 ...