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++ 互斥的更多相关文章

  1. ucos实时操作系统学习笔记——任务间通信(互斥锁)

    想讲一下ucos任务间通信中的mutex,感觉其设计挺巧妙,同sem一样使用的是event机制实现的,代码不每一行都分析,因为讲的没邵贝贝老师清楚,主要讲一下mutex的内核是如何实现的.可以理解互斥 ...

  2. 多线程之互斥锁(By C++)

    首先贴一段win32API实现的多线程的代码,使用CreateThread实现,如果不要传参数,就把第四个参数设为NULL #include<Windows.h> #include< ...

  3. Java基础加强之多线程篇(线程创建与终止、互斥、通信、本地变量)

    线程创建与终止 线程创建 Thread类与Runnable接口的关系 public interface Runnable { public abstract void run(); } public ...

  4. 简单的JavaScript互斥锁

    去年有几个项目需要使用JavaScript互斥锁,所以写了几个类似的,这是其中一个: //Published by Indream Luo //Contact: indreamluo@qq.com / ...

  5. C#互斥体——Mutex

    Mutex对象是一个同步基元,可以用来做线程间的同步. 若多个线程需要共享一个资源,可以在这些线程中使用Mutex同步基元.当某一个线程占用Mutex对象时,其他也需要占用Mutex的线程将处于挂起状 ...

  6. Java多线程系列--“JUC锁”02之 互斥锁ReentrantLock

    本章对ReentrantLock包进行基本介绍,这一章主要对ReentrantLock进行概括性的介绍,内容包括:ReentrantLock介绍ReentrantLock函数列表ReentrantLo ...

  7. 异步与并行~ReaderWriterLockSlim实现的共享锁和互斥锁

    返回目录 在System.Threading.Tasks命名空间下,使用ReaderWriterLockSlim对象来实现多线程并发时的锁管理,它比lock来说,性能更好,也并合理,我们都知道lock ...

  8. 嵌入式Linux驱动学习之路(十四)按键驱动-同步、互斥、阻塞

    目的:同一个时刻,只能有一个应用程序打开我们的驱动程序. ①原子操作: v = ATOMIC_INIT( i )  定义原子变量v并初始化为i atomic_read(v)        返回原子变量 ...

  9. 进程互斥和fork

    自父进程继承 进程的资格(真实(real)/有效(effective)/已保存(saved) 用户号(UIDs)和组号(GIDs)) 环境(environment) 堆栈 内存 打开文件的描述符(注意 ...

  10. Delphi多线程编程--线程同步的方法(事件、互斥、信号、计时器)简介

    更详细的可以参考:http://www.cnblogs.com/xumenger/p/4450659.html 或者参考之后的博客 四个系统内核对象(事件.互斥.信号.计时器)都是线程同步的手段,从这 ...

随机推荐

  1. vs 工程中替换 Qt 静态库

    上篇介绍了如何编译 Qt 静态库 编译 windows 上的 qt 静态库 这篇介绍如何替换已有的 Qt 静态库,比如 Qt5.15.0 有很多 bug,我们不得不提升 Qt 版本来避免 bug 导致 ...

  2. Golang从入门到跑路-从基础到微服务学习路线图

    收录的awesome-go项目,学习基础系列,go项目实战,go源码分析,go开发者成长路线图等等,把他们收集起来学习. 地址:https://github.com/jiujuan/go-collec ...

  3. Flink本地模式安装和使用

    Flink官方主页:https://flink.apache.org/ . 安装部署 不同版本的Flink对JDK版本要求不尽相同,需要根据具体的Flink版本要求先安装好JDK环境. 通常,在Fli ...

  4. NSIS制作安装包笔记(二):NSIS使用NSIS+Qt界面制作安装包流程

    前言   Nsis可以使用duilib也可以使用qt界面,笔者主要qt,本文章梳理nsis+qt制作安装包的基本流程.   下载Nsis-Ui-Plugin插件   Github地址:https:// ...

  5. 第123篇: JS函数属性与方法

    好家伙,本篇为<JS高级程序设计>第十章"函数"学习笔记 ECMAScript 中的函数是对象,因此有属性和方法. 1.函数属性 每个函数都有两个属性:length 和 ...

  6. C++ 深拷贝浅拷贝

    C++ 深拷贝浅拷贝 C++默认生成的拷贝构造函数,他的行为就是浅拷贝,他只会复制一个一模一样的的指针,并不会操作指针指向的东西. 要想实现我们的逻辑需求,就要自定义拷贝构造函数,实现深拷贝. 我们来 ...

  7. Java面试挂在线程创建后续,不要再被八股文误导了!创建线程的方式只有1种

    写在开头 在上篇博文中我们提到小伙伴去面试,面试官让说出8种线程创建的方式,而他只说出了4种,导致面试挂掉,在博文中也给出了10种线程创建的方式,但在文章的结尾我们提出:真正创建线程的方式只有1种,剩 ...

  8. 小工具 --- 百度翻译API翻译工具

    引言 最近想把一些英文官方文档的资料翻译成中文,然后转化为Markdown文档,然后发现百度通用翻译的API有不错的免费额度,个人申请也能申请到高级版.这个额度足够个人的日常使用了. 如何使用 如何使 ...

  9. 2024-03-09:用go语言,我们把无限数量的栈排成一行,按从左到右的次序从 0 开始编号, 每个栈的的最大容量 capacity 都相同。实现一个叫「餐盘」的类 DinnerPlates, Di

    2024-03-09:用go语言,我们把无限数量的栈排成一行,按从左到右的次序从 0 开始编号, 每个栈的的最大容量 capacity 都相同.实现一个叫「餐盘」的类 DinnerPlates, Di ...

  10. 选择单词后 按 ctrl + space 单词发音

    需求: 在ide或其他地方,经常有单词发音不是很确定,但并不要很详细 就听个单词发音. 确定快捷键: 左手单手操作,我键盘上貌似就 左边的ctrl和空格键 还没有设定 翻译软件: 使用 pc端的 欧路 ...