C++多线程快速入门(四)shared_mutex以及读写锁应用

在前面的三讲中我们使用的mutex都是普通的std::mutex,这里介绍一下shared_mutex,版本为C++17
std::shared_mutex的底层实现时操作系统提供的读写锁,在读多写少的情况下,该shared_mutexmutex更加高效。

它提供了常用的四种方法:

lockunlock分别用于获取写锁和解除写锁

lock_sharedunlock_shared分别用于获取读锁和解除读锁

写锁模式称为排他锁,读锁模式称为共享锁。

c++11和c++14标准中分别引入unique_lockshared_lock两个类模板配合shared_mutex使用。

对象在构造时自动对std::shared_mutex加锁,析构时自动对其解锁。

前者用于加解写锁,后者用于加解读锁。

当然在第二讲中,我们也谈到了unique_lock对于普通锁mutex的一种应用,当时是和std::lock_guard对比的。
下面是案例代码,对于共享资源我们创建多个读线程和一个写线程,分别使用std::mutexstd::shared_mutex做一下性能测试。

#include <iostream>
#include <thread>
#include <mutex>
#include <shared_mutex>// 读线程数量
#define READER_THREAD_COUNT 4
// 最大循环次数
#define LOOP_COUNT 500000class shared_mutex_counter {
// 这里使用的锁是 shared_mutex
private:mutable std::shared_mutex m_mutex;unsigned int m_value = 0;     // 多个线程共享的资源
public:shared_mutex_counter() = default;~shared_mutex_counter() = default;// shared_lock  同一时可多个读线程可以同时访问m_value的值unsigned int get() const{// 这里使用shared_lockstd::shared_lock<std::shared_mutex> lock(m_mutex);return m_value;}// unique_lock 同一时间仅有一个写线程可以修改m_value的值void incremet() {std::unique_lock<std::shared_mutex> lock(m_mutex);m_value++;}
};class mutex_counter {
// 这里使用的锁是 shared_mutex
private:mutable std::mutex m_mutex;unsigned int m_value = 0;     // 多个线程共享的资源
public:mutex_counter() = default;~mutex_counter() = default;// 同一时间仅有一个读线程可以同时访问m_value的值unsigned int get() const{std::unique_lock<std::mutex> lock(m_mutex);return m_value;}// 同一时间仅有一个写线程可以修改m_value的值void incremet(){std::unique_lock<std::mutex> lock(m_mutex);m_value++;}
};// 测试 shared_mutex
void test_shared_mutex()
{shared_mutex_counter counter;unsigned int temp;// 写线程函数auto write = [&counter]() {for (int i = 0; i < LOOP_COUNT; i++) {counter.incremet();}};// 读线程函数auto read = [&counter, &temp]() {for (int i = 0; i < LOOP_COUNT; i++) {temp = counter.get();}};// 存放读线程对象指针的数组std::thread** tarray = new std::thread* [READER_THREAD_COUNT];// 记录起始时间clock_t start = clock();// 创建 READER_THREAD_COUNT 个读线程for (int i = 0; i < READER_THREAD_COUNT; i++) {tarray[i] = new std::thread(read);}// 创建一个写线程std::thread tw(write);// 等待joinfor (int i = 0; i < READER_THREAD_COUNT; i++) {tarray[i]->join();}tw.join();clock_t end = clock();std::cout << "test shared_mutex" << std::endl;std::cout << "thread count " << READER_THREAD_COUNT << std::endl;std::cout << "spend time " << end - start << std::endl;std::cout << "result : " << counter.get() << std::endl;std::cout << "temp : " << temp << std::endl;
}// 测试 mutex
void test_mutex()
{mutex_counter counter;unsigned int temp;// 写线程函数auto write = [&counter]() {for (int i = 0; i < LOOP_COUNT; i++) {counter.incremet();}};// 读线程函数auto read = [&counter, &temp]() {for (int i = 0; i < LOOP_COUNT; i++) {temp = counter.get();}};// 存放读线程对象指针的数组std::thread** tarray = new std::thread* [READER_THREAD_COUNT];// 记录起始时间clock_t start = clock();// 创建 READER_THREAD_COUNT 个读线程for (int i = 0; i < READER_THREAD_COUNT; i++) {tarray[i] = new std::thread(read);}// 创建一个写线程std::thread tw(write);// 等待joinfor (int i = 0; i < READER_THREAD_COUNT; i++) {tarray[i]->join();}tw.join();clock_t end = clock();std::cout << "test mutex" << std::endl;std::cout << "thread count " << READER_THREAD_COUNT << std::endl;std::cout << "spend time " << end - start << std::endl;std::cout << "result : " << counter.get() << std::endl;std::cout << "temp : " << temp << std::endl;
}int main() {test_mutex();std::cout << std::endl;test_shared_mutex();return 0;
}

测试结果如下:

test mutex
thread count 8
spend time 1312
result : 500000
temp : 500000test shared_mutex
thread count 8
spend time 601
result : 500000
temp : 475766

有个问题,我同样程序在windows的clion和vs上跑结果差异较大,clion跑出来的结果shared_mutex耗时比mutex多好多。
linux下的vscode跑出来结果正常。

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

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

相关文章

Tornado/Python 学习笔记(一)

1.源代码下载及安装&#xff1a;http://www.tornadoweb.org/en/stable/ 2.python中xmlrpc库官方文档&#xff1a;https://docs.python.org/3/library/xmlrpc.html?highlightxmlrpc 3.xml介绍与学习&#xff1a;http://www.w3school.com.cn/xml/xml_intro.asp XML 被设计为传输…

spring-aop-annotation

1。假设你已经配好依赖注入那一块。此时的bean.xml为 <?xml version"1.0" encoding"UTF-8"?><beans xmlns"http://www.springframework.org/schema/beans"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xmlns:cont…

c++实现简单线程池代码

目录完整代码TaskPool.cppTaskPool.hmain.cpp完整代码 TaskPool.cpp // // Created by LENOVO on 2021/10/25. //#include "TaskPool.h" #include <functional>std::mutex printMutex;TaskPool::TaskPool() : m_bRunning(false) {}TaskPool::~TaskPool() {re…

Android静态图片人脸识别的完整demo(附完整源码)

Demo功能&#xff1a;利用android自带的人脸识别进行识别&#xff0c;标记出眼睛和人脸位置。点击按键后进行人脸识别&#xff0c;完毕后显示到imageview上。 第一部分&#xff1a;布局文件activity_main.xml [html] view plaincopyprint?<RelativeLayout xmlns:android&qu…

图论:最短路径搜索--Dijkstra算法(c代码实现)

最近因为辞职&#xff0c;有不少闲功夫&#xff0c;重温下数据结构&#xff0c;顺便练练手。今天说说最短路径搜索算法中的Dijkstra原理和实现。 一&#xff1a;简介 这个算法用于解决图中单源最短路径问题。所谓单源节点是指给定源节点&#xff0c;求图中其它节点到此源节点的…

C++多线程快速入门(五)简单线程池设计

目录设计思路主线程运行逻辑task以及taskpool设计详细流程讲解完整代码打印结果往期回顾设计思路 线程池实际上就是一组线程&#xff0c;当我们需要异步执行一些任务时&#xff0c;经常要通过OS频繁创建和销毁线程&#xff0c;不如直接创建一组在程序生命周期内不会退出的线程…

C--函数

函数:具有特定功能的代码段,分为库函数,自定义函数. 函数定义: 函数返回值类型 函数名(形式参数列表) { 代码段; return 返回值; } 注意:每个函数返回值最多只有一个.return是一个函数结束的标志. 形式参数(形参):函数定义时使用的虚拟参数名,用以接收函数调用是传递过来的实际…

公式系统 - TradeBlazer公式基础 - Bar数据

Bar数据 在介绍Bar数据之前&#xff0c;首先&#xff0c;我们需要讨论一下TradeBlazer公式的计算方法&#xff0c;针对上面介绍的各种公式类型&#xff0c;包含公式应用&#xff0c;在公式进行计算时&#xff0c;都是建立在基本数据源(Bar数据)之上&#xff0c;我们这里所谓的B…

C++网络编程快速入门(一):TCP网络通信基本流程以及基础函数使用

目录流程概述服务器端代码实现客户端代码实现函数和结构讲解sockaddr_in和sockaddrsocket &#xff1a; 创建一个socket连接bind &#xff1a;绑定地址以及端口号问题流程概述 客户端与服务器之间的网络通信基本原理如下所示&#xff0c;复杂一点的架构可能会添加消息中间件。…

php 字符串处理

addcslashes — 为字符串里面的部分字符添加反斜线转义字符addslashes — 用指定的方式对字符串里面的字符进行转义bin2hex — 将二进制数据转换成十六进制表示chop — rtrim() 的别名函数chr — 返回一个字符的ASCII码chunk_split — 按一定的字符长度将字符串分割成小块conve…

使用前端框架Foundation 4来帮助简化响应式设计开发

日期&#xff1a;2013-3-12 来源&#xff1a;GBin1.com Foundation是一套使用广泛的前端开发套件&#xff0c;可以帮助你快速的网站。最近ZURB发布了一个新版本的Foundation 4前端框架&#xff0c;能够有效的帮助你快速的开发响应式的网站。 和另外一个套知名的前端框架BootSt…

C++网络编程快速入门(二):Linux下使用select演示简单服务端程序

目录select参数解释select使用规范select使用缺点基本流程实例代码通信效果演示往期文章select参数解释 extern int select (int __nfds, fd_set *__restrict __readfds,fd_set *__restrict __writefds,fd_set *__restrict __exceptfds,struct timeval *__restrict __timeout)…

Android转载一:Android文件命名规范

REF&#xff1a;http://blog.csdn.net/gulianchao/article/details/23391651 (一) Layout命名 1&#xff0e;contentview命名&#xff1a;activity_功能模块.xml 例如&#xff1a;activity_main.xml、activity_more.xml 2&#xff0e;Dialog命名&#xff1a;dialog_描述.xml …

[转]XBRL应用软件分类

1) 分类标准编辑软件(Taxonomy editor)&#xff1a; 分类标准是XBRL技术的应用基础&#xff0c;每一个采用XBRL技术的国家都必须先按各国的GAAP制订XBRL分类标准&#xff0c;上市公司才能据以编制实例文件。由于一套XBRL 2.0或2.1版分类标准必须包含至少一份XML Schema文…

C++网络编程快速入门(三):阻塞与非阻塞式调用网络通信函数

目录阻塞与非阻塞定义send与recvconnect一些问题为什么要将监听socket设置为非阻塞阻塞与非阻塞定义 阻塞模式指的是当前某个函数执行效果未达预期&#xff0c;该函数会阻塞当前的执行线程&#xff0c;程序执行流在超时时间到达或者执行成功后恢复原有流程。非阻塞模式相反&am…

css3实现头像旋转360度

css样式: .div a img{ width: 88px; height: 88px; border-radius: 88px; transition: all 1.2s ease-out 0s;}.div a img:hover{ -webkit-transform:rotate(360deg); -moz-transform:rotate(360deg); -o-transform:rotate(360deg); -ms-transform:rotate(360deg); transform:r…

POJ 2488 深搜

POJ 2488 深搜 要求字典序的顺序。 1 #include <iostream>2 #include <stdio.h>3 #include <string.h>4 using namespace std;5 int n,m,cnt;6 bool success;7 bool sign[30][30];8 int step[30][2];9 int dir[8][2]{ 10 -2,-1,-2,1, 11 …

socket 端口和地址复用

https://blog.csdn.net/weibo1230123/article/details/79978745 https://blog.csdn.net/weixin_42157432/article/details/115560824 在linux socket网络编程中&#xff0c;大规模并发TCP或UDP连接时&#xff0c;经常会用到端口复用&#xff1a; int opt 1; if (setsockopt…

MyEclipse老是弹出problem occurred窗口

有的时候是因为jsp页面中的java脚本有误&#xff0c;比如说<% String name"";>就会出现错误&#xff0c;因为结束标签少了一个百分号&#xff05;。转载于:https://www.cnblogs.com/passer1991/archive/2013/03/15/2961624.html

C++网络编程快速入门(四):EPOLL模型使用

目录基本使用方法step1:创建epollfdstep2:将fd绑定到epollfdstep3:调用epoll_wait检测事件epoll_wait与poll、select区别所在水平触发与边缘触发基本使用方法 step1:创建epollfd 创建一个epollfd&#xff0c;若epoll_create调用成功&#xff0c;则返回一个非负值的epollfd&am…