跟我学C++中级篇——std::shared_lock

一、读写锁

有过多语言编程的开发人员,往往在别的语言中(如Java等)看到过读写锁,它特别适合于在写少读多的情况下。一般在互联网的应用场景中,这种读非常多,写很少的场景非常普遍,所以读写锁其实应用非常多。
在C++中,原来没有这种所谓读写锁之说,只有轻量级锁和重量级锁。虽然可以通过这些锁来模拟实现读写锁,但毕竟不如原生库中带着好用。STL的发展就是要与时俱进,也要看到实际的应用需求,所以在C++14中推出了std::shared_lock。
std::shared_lock是一个通用的共享互斥体的封装,它允许延迟锁定、定时锁定和锁所有权的转移。其中使用的共享互斥体类似std::unique_lock中使用的排它性互斥体,有对比就好理解。因为它的锁是可共享的,所以它就实现了读锁的处理。写锁自然可以使用排它型锁std::unique_lock 或std::lock_gurad。

二、std::shared_lock

std::shared_lock的原理其实比较简单,先看一下其定义:

  template<typename _Mutex>class shared_lock{public:typedef _Mutex mutex_type;// Shared lockingshared_lock() noexcept : _M_pm(nullptr), _M_owns(false) { }explicitshared_lock(mutex_type& __m): _M_pm(std::__addressof(__m)), _M_owns(true){ __m.lock_shared(); }shared_lock(mutex_type& __m, defer_lock_t) noexcept: _M_pm(std::__addressof(__m)), _M_owns(false) { }shared_lock(mutex_type& __m, try_to_lock_t): _M_pm(std::__addressof(__m)), _M_owns(__m.try_lock_shared()) { }shared_lock(mutex_type& __m, adopt_lock_t): _M_pm(std::__addressof(__m)), _M_owns(true) { }template<typename _Clock, typename _Duration>shared_lock(mutex_type& __m,const chrono::time_point<_Clock, _Duration>& __abs_time): _M_pm(std::__addressof(__m)),_M_owns(__m.try_lock_shared_until(__abs_time)) { }template<typename _Rep, typename _Period>shared_lock(mutex_type& __m,const chrono::duration<_Rep, _Period>& __rel_time): _M_pm(std::__addressof(__m)),_M_owns(__m.try_lock_shared_for(__rel_time)) { }~shared_lock(){if (_M_owns)_M_pm->unlock_shared();}shared_lock(shared_lock const&) = delete;shared_lock& operator=(shared_lock const&) = delete;shared_lock(shared_lock&& __sl) noexcept : shared_lock(){ swap(__sl); }shared_lock&operator=(shared_lock&& __sl) noexcept{shared_lock(std::move(__sl)).swap(*this);return *this;}voidlock(){_M_lockable();_M_pm->lock_shared();_M_owns = true;}booltry_lock(){_M_lockable();return _M_owns = _M_pm->try_lock_shared();}template<typename _Rep, typename _Period>booltry_lock_for(const chrono::duration<_Rep, _Period>& __rel_time){_M_lockable();return _M_owns = _M_pm->try_lock_shared_for(__rel_time);}template<typename _Clock, typename _Duration>booltry_lock_until(const chrono::time_point<_Clock, _Duration>& __abs_time){_M_lockable();return _M_owns = _M_pm->try_lock_shared_until(__abs_time);}voidunlock(){if (!_M_owns)__throw_system_error(int(errc::resource_deadlock_would_occur));_M_pm->unlock_shared();_M_owns = false;}// Settersvoidswap(shared_lock& __u) noexcept{std::swap(_M_pm, __u._M_pm);std::swap(_M_owns, __u._M_owns);}mutex_type*release() noexcept{_M_owns = false;return std::__exchange(_M_pm, nullptr);}// Gettersbool owns_lock() const noexcept { return _M_owns; }explicit operator bool() const noexcept { return _M_owns; }mutex_type* mutex() const noexcept { return _M_pm; }private:void_M_lockable() const{if (_M_pm == nullptr)__throw_system_error(int(errc::operation_not_permitted));if (_M_owns)__throw_system_error(int(errc::resource_deadlock_would_occur));}mutex_type*	_M_pm;bool		_M_owns;};

std::shared_lock有两个明显的特点,也就是和读写锁对应。一个是读的共享性;另外一个是定的独占性。看它的源码中的实现,其实也是猜测的那样,可以使用各种支持共享的互斥体和相关时间状态以及各种对锁的处理策略。没有什么让人一看就迷糊的代码,相对其它的模板类,算是很友好了。

三、例程

看一个例程:

#include <iostream>
#include <mutex>
#include <shared_mutex>
#include <string>
#include <thread>std::string file = "Original content."; // Simulates a file
std::mutex output_mutex; // mutex that protects output operations.
std::shared_mutex file_mutex; // reader/writer mutexvoid read_content(int id)
{std::string content;{std::shared_lock lock(file_mutex, std::defer_lock); // Do not lock it first.lock.lock(); // Lock it here.content = file;}std::lock_guard lock(output_mutex);std::cout << "Contents read by reader #" << id << ": " << content << '\n';
}void write_content()
{{std::lock_guard file_lock(file_mutex);file = "New content";}std::lock_guard output_lock(output_mutex);std::cout << "New content saved.\n";
}int main()
{std::cout << "Two readers reading from file.\n"<< "A writer competes with them.\n";std::thread reader1{read_content, 1};std::thread reader2{read_content, 2};std::thread writer{write_content};reader1.join();reader2.join();writer.join();std::cout << "The first few operations to file are done.\n";reader1 = std::thread{read_content, 3};reader1.join();
}

其运行的结果可能是:

Two readers reading from file.
A writer competes with them.
Contents read by reader #1: Original content.
Contents read by reader #2: Original content.
New content saved.
The first few operations to file are done.
Contents read by reader #3: New content

在实现类似上面的代码中还可以使用std::shared_timed_mutex等相关的互斥体,更有利于精细化的锁的粒度控制。上面的代码有一点点小意外,std::shared_mutex是在C++17才推出的,如果在C++14想使用std::shared_lock,就得使用std::shared_timed_mutex了。同时,大家需要注意输出的锁,仍然是一个独占性锁。
另外,读写同时操作时,有可能出现读写不一致的情况,所以可以应用在不太在意修改的流程中,比如定时刷新显示当前的客户记录,虽然可能修改后的数据未能在第一时间显示到界面上,但其实再刷新一次就会好。
当然,如果需要强制性的一致,那么还是使用排它锁比较好。

四、总结

语言是实现设计的手段和工具,不要太在意它们的不同。选择更合适的实现和达到最终的结果,这才是一个优秀的开发人员需要做到的。而语言中的整体的设计和开发方向其实受计算机的底层限制很大,再加上几乎所有的语言设计者都是精通计算机系统的,其处理问题的思想本质也是相通的。所以,一时的不同,不代表未来的不同;实现的不同,不代表应用的表现不同。
与诸君共勉!

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

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

相关文章

VScode 只能运行c,运行不了c++的解决问题

原文链接&#xff1a;Vscode只能运行c&#xff0c;运行不了c的解决方法 VScode 只能运行c&#xff0c;运行不了c&#xff0c;怎么回事呢&#xff0c;解决问题&#xff1a; 在tasks.json中加上“"-lstdc"”&#xff0c; 这样之后 要重启VScode&#xff0c;点击链接…

driftingblues6靶机

打开靶场 查看页面源代码&#xff0c;最下面有一个注释&#xff0c;提供了一个网址 vmlist.github.io&#xff0c;我们去访问一下 这里是一个github页面&#xff0c;提供攻防虚拟机的下载&#xff0c;对我们解题并没有什么有用的信息&#xff0c;我们再去扫描端口 发现只有80端…

YOLOv5部署到web端(flask+js简单易懂)

文章目录 前言最终实现效果图后端实现 主界面检测函数检测结果显示 前端实现 主界面(index.html&#xff09;显示图片界面 总结 前言 最近&#xff0c;老板让写一个程序把yolov5检测模型部署到web端&#xff0c;在网页直接进行目标检测。经过1个星期的努力&#xff0c;终于实…

基本算法——聚类

目录 创建工程 加载数据 聚类算法 评估 完整代码 结论 相比于有监督的分类器&#xff0c;聚类的目标是从一组未打标签的数据中识别相似对象组。它可 以用于识别同类群体的代表性样本&#xff0c;找到有用与合适的分组&#xff1b;或者找到不寻常的样本&#xff0c;比如 异…

安装教程:慧集通集成平台(DataLinkX)智能体客户端安装操作(Linux/windows/mac)

1.下载客户端 使用提供的账号登录集成平台后台(https://www.datalinkx.cn/),点击左侧菜单栏【智能体】→【智能体】进入到智能体列表界面&#xff0c;在该界面我们找到功能栏中的下载按钮点击则会弹出下载界面&#xff0c;在该界面我们可以选择不同的系统操作系统来下载对应版…

【Rust自学】8.4. String类型 Pt.2:字节、标量值、字形簇以及字符串的各类操作

8.4.0. 本章内容 第八章主要讲的是Rust中常见的集合。Rust中提供了很多集合类型的数据结构&#xff0c;这些集合可以包含很多值。但是第八章所讲的集合与数组和元组有所不同。 第八章中的集合是存储在堆内存上而非栈内存上的&#xff0c;这也意味着这些集合的数据大小无需在编…

1、pycharm、python下载与安装

1、去官网下载pycharm 官网&#xff1a;https://www.jetbrains.com/pycharm/download/?sectionwindows 2、在等待期间&#xff0c;去下载python 进入官网地址&#xff1a;https://www.python.org/downloads/windows/ 3、安装pycharm 桌面会出现快捷方式 4、安装python…

静默模式下安装Weblogic 14.1.1.0.0

目录 一、下载weblogic安装包二、安装JDK三、安装weblogic1、创建weblogic用户2、创建weblogic的安装目录3、上传并解压weblogic安装包4、创建 oraInst.loc 文件5、创建 wls.rsp 响应文件6、静默安装weblogic7、创建域8、启动Weblogic9、设置防火墙规则,以便其他服务器访问10、…

Windows安装Confluence详解

Confluence官网下载地址&#xff1a;https://www.atlassian.com/software/confluence/download-archives 建议安装confluence版本下载5.0-7.0之间&#xff0c;比较稳定一点&#xff0c;我安装的是6.8.2版本 centos7系统和阿里云服务安装后太卡了&#xff0c;果断放弃 Conflu…

Unity is running as administrator解决办法

每次打开Unity项目都会有这个弹窗 解决办法&#xff1a; 打开本地安全策略 - 安全选项 &#xff0c;把 用户账户控制&#xff1a;以管理员批准模式运行所有管理员 用户账户控制&#xff1a;用于内置管理员账户的管理员批准模式 改成已启用就行

springboot+vue实现SSE服务器发送事件

思路 一个基于订阅发布机制的SSE事件。客户端可以请求订阅api&#xff08;携带客户端id&#xff09;&#xff0c;与服务器建立SSE链接&#xff1b;后续服务器需要推送消息到客户端时&#xff0c;再根据客户端id从已建立链接的会话中找到目标客户端&#xff0c;将消息推送出去。…

【阻塞队列】- ArrayBlockingQueue 的原理-迭代器

文章目录 1. 前言2. 迭代器3. Itrs3.1 参数3.2 迭代器 Itr3.2.1 参数3.2.2 构造器3.2.3 hasNext3.2.4 next3.2.5 remove3.2.6 shutdown3.2.7 removedAt3.2.8 takeIndexWrapped 3.3 doSomeSweeping&#xff08;tryHandler&#xff09;3.4 register3.5 takeIndexWrapped3.6 remov…

人工智能与传统编程的主要区别是什么?

传统编程&#xff1a;开发者预先编写软件行为规则&#xff0c;代码基于程序员定义逻辑处理输入并产生确定输出&#xff0c;具有确定性、手动编写规则和结构化逻辑特点&#xff0c;如垃圾邮件分类程序基于预设关键词等规则。AI 编程&#xff1a;从数据中学习而非手动编写规则&am…

SpringBoot使用TraceId日志链路追踪

项目场景&#xff1a; ??有时候一个业务调用链场景&#xff0c;很长&#xff0c;调了各种各样的方法&#xff0c;看日志的时候&#xff0c;各个接口的日志穿插&#xff0c;确实让人头大。为了解决这个痛点&#xff0c;就使用了TraceId&#xff0c;根据TraceId关键字进入服务…

ts总结一下

ts基础应用 /*** 泛型工具类型*/ interface IProps {id: string;title: string;children: number[]; } type omita Omit<IProps, id | title>; const omitaA: omita {children: [1] }; type picka Pick<IProps, id | title>; const pickaA: picka {id: ,title…

八大排序——直接插入排序

直接插入排序&#xff08;Straight Insertion Sort&#xff09;&#xff0c;通常简称为插入排序&#xff0c;是一种简单直观的排序算法。它的工作原理是通过构建有序序列&#xff0c;对于未排序数据&#xff0c;在已排序序列中从后向前扫描&#xff0c;找到相应位置并插入。本文…

【QGIS入门实战精品教程】7.3:QGIS制作千层饼(DEM+等高线+影像+TIN)

文章目录 一、效果展示二、数据准备三、制作过程1. 打开软件2. 添加图层3. 制作千层饼一、效果展示 二、数据准备 订阅专栏后,从专栏配套案例数据包中的7.3.rar中获取。 1. dem 2. 影像 3. 等高线 4. tin 三、制作过程 1. 打开软件 打开QGIS软件。 QGIS软件主界面。

NetSuite Formula(HTML)超链打开Transaction

当Saved Search作为Sublist应用在Form时&#xff0c;如果Document Number是Group过的&#xff0c;则会出现如下超链失效的情况。 解决办法&#xff1a; 可以利用Saved Search中的Formula&#xff08;HTML&#xff09;功能来构建超链&#xff0c;用于打开Transaction。 以下图…

Springboot3.x整合swagger3

在网上看了许多教程&#xff0c;发现很多都是针对Spring Boot 2 框架的&#xff0c;即使有针对Spring Boot 3 的&#xff0c;用法也不太一样,配置项经常找不到类&#xff0c;经过对比测试&#xff0c;最后我使用的是 SpringDoc OpenAPI Starter WebMvc UI. pom为 <!--swag…

android.enableJetifier=true的作用:V4包的类自动编程成了androidx包的类,实现androidx的向下兼容

结论&#xff1a;引入androidx包后&#xff0c;可以兼容旧版本v4包的插件&#xff0c;把之前的 implementation com.yinglan.alphatabs:library:1.0.8 引入的组件中使用v4包的类&#xff0c;里面V4包自动反编译成 androidx包的类 结论; ‌V4包的类自动编程成了androidx包的…