当前位置: 首页 > news >正文

[C/C++线程安全]_[中级]_[多线程如何使用共享锁提升性能]

场景

  1. C/C++多线程程序里,经常会用到对共享变量进行读写,最容易想到的是加上锁进行读写。假设当有多个线程对共享变量进行读操作时,该业务逻辑的耗时需要至少2s来进行处理,那么当一个读线程进行锁定时,其他读线程只能等待2s才会被唤醒。因为读共享变量是线程安全的,有没有方法在读的时候不需要排他锁占用?

说明

  1. C/C++14开始,提供了一个std::shared_lock共享锁,需要传入一个共享互斥量std::shared_mutex来进行读写锁操作。这个共享锁提升了多线程读共享变量的性能。
static shared_mutex rwMutex;
static string gData;...
shared_lock<shared_mutex> lock(rwMutex);
std::string str(gData);
  1. std::shared_lock的作用就是在一个线程获取到共享锁的时候,在没释放前,其他读线程可以继续获取这个共享锁,而写线程想要获取这个mutex的排他锁定,只能进入等待。
unique_lock<shared_mutex> lock(rwMutex);
gData.append(to_string(gAccumulate++)).append(":");
  1. std::shared_mutex可以作为std::unique_lock锁的参数进行排他锁定。 如果有多个写线程,那么其他写线程只能等待第一个写线程操作结束才会被唤醒去获取排他锁。

  2. C++并没有编译时的线程安全检查,默认不禁止数据竞争,如果发生数据竞争时,结果是未定义的,也就是可能导致系统崩溃。 要保证没有数据竞争,只能靠严格的执行库的使用规则,这点即使高级程序员也容易偶尔犯错。希望在未来的标准里可以加上之类编译时检查。

  3. 还有一点就是尽量在加锁操作时使用RAII的特性,比如使用类shared_lockunique_lock来创建局部变量。这样锁定和解锁会成对执行,而不是调用shared_mutexlock(),lock_shared()unlock(),unlock_shared()方法。

例子

  1. 这里写了两个方法来对比了共享锁和排他锁的性能。很明显的,共享锁大大的提高了性能。
// test-shared-lock.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//#include <iostream>
#include <shared_mutex>
#include <thread>
#include <string>
#include <sstream>
#include <vector>
#include <mutex>
#include <chrono>
#include <condition_variable>using namespace std;// 唯一锁
static mutex uMutex;// 读写锁
static shared_mutex rwMutex;
static string gData;static mutex rMutex;
static stringstream rData;
static condition_variable rCond;static int gAccumulate;static chrono::steady_clock::time_point Cpp11FuncBegin()
{return chrono::steady_clock::now();
}static double Cpp11FuncEnd(chrono::steady_clock::time_point t)
{auto elapse = chrono::steady_clock::now() - t;chrono::duration<double,milli> mi = elapse;return mi.count();
}template<class T,class S>
void FuncReader(int index,S& s)
{{std::unique_lock<mutex> lock(rMutex);rCond.wait(lock);}auto tPoint = Cpp11FuncBegin();//shared_lock<shared_mutex> lock(rwMutex);T lock(s);std::string str(gData);// -- 增加这个是为了看共享锁是否能同时进入; 如果能同时进入那么执行时间大概是2000ms左右.std::this_thread::sleep_for(std::chrono::milliseconds(2000)); auto result = Cpp11FuncEnd(tPoint);std::unique_lock<mutex> myLock(rMutex);rData << "index: " << index << " Read: " << str << " Duration: " << result << "ms \n";}static void FuncWriter(int index)
{{std::unique_lock<mutex> lock(rMutex);rCond.wait(lock);}unique_lock<shared_mutex> lock(rwMutex);gData.append(to_string(gAccumulate++)).append(":");std::unique_lock<mutex> myLock(rMutex);rData << "index" << index << " Write: " << gData << "\n";}// -- 读写共享锁
static void TestReadWriteSharedData()
{cout << "======== TestReadWriteSharedData ==========" << endl;rData.clear();gData.clear();vector<thread> tasks;int i = 0;for (; i < 8; ++i) tasks.emplace_back(FuncReader<shared_lock<shared_mutex>,shared_mutex>,i,std::ref(rwMutex));for(; i < 10; ++i)tasks.emplace_back(FuncWriter,i);rCond.notify_all();for (auto& one : tasks)one.join();auto str = rData.str();cout << str << endl;
}// -- 普通的非共享锁
static void TestUniqueLock()
{cout << "======== TestUniqueLock ==========" << endl;rData.clear();rData.str("");gData = "0;";vector<thread> tasks;int i = 0;for (; i < 4; ++i) tasks.emplace_back(FuncReader<unique_lock<mutex>,mutex>,i,std::ref(uMutex));rCond.notify_all();for (auto& one : tasks)one.join();auto str = rData.str();cout << str << endl;
}int main()
{cout << "Hello World!\n";TestReadWriteSharedData();TestUniqueLock();
}

输出

Hello World!
======== TestReadWriteSharedData ==========
index9 Write: 0:
index8 Write: 0:1:
index: 1 Read: 0:1: Duration: 2005.57ms
index: 6 Read: 0:1: Duration: 2005.72ms
index: 0 Read: 0:1: Duration: 2005.57ms
index: 7 Read: 0:1: Duration: 2005.57ms
index: 2 Read: 0:1: Duration: 2005.6ms
index: 3 Read: 0:1: Duration: 2005.63ms
index: 4 Read: 0:1: Duration: 2005.64ms
index: 5 Read: 0:1: Duration: 2005.67ms======== TestUniqueLock ==========
index: 3 Read: 0; Duration: 2005.35ms
index: 2 Read: 0; Duration: 4012.66ms
index: 0 Read: 0; Duration: 6021.45ms
index: 1 Read: 0; Duration: 8031.52ms

参考

  1. std::shared_lock

  2. std::shared_mutex

  3. 多线程访问修改集合vector会冲突的两个解决方案

  4. C++实现synchronized方式的对象锁

  5. C++11语言特性和标准库

  6. C++14语言特性和标准库

http://www.dtcms.com/a/323399.html

相关文章:

  • EP06:【DL 第二弹】动态计算图与梯度下降入门
  • 【C/C++】具有C风格的强制类型转换:显式类型转换、隐式类型转换,C语言强制类型转换
  • 第六章:【springboot】框架springboot原理、springboot父子工程与Swagger
  • 算法题(183):质量检测
  • 《答客难》东方朔
  • 网络原理-初识
  • FreeRTOS入门知识(初识RTOS任务调度)(三)
  • AVL树的四种旋转
  • 【Python 语法糖小火锅 · 第 4 涮】
  • 资深全栈工程师面试题总结
  • 【牛客刷题】小红的区间删除
  • 第16届蓝桥杯Scratch选拔赛初级及中级(STEMA)2024年11月24日真题
  • Linux之shell脚本篇(四)
  • SQL 172 未完成试卷数大于1的有效用户
  • 9. 堆和栈有什么区别
  • 01数据结构-图的邻接矩阵和遍历
  • 从零开始理解编译原理:设计一个简单的编程语言
  • svg 图片怎么设置 :hover 时变色
  • 交 换
  • sigaction 中 sa_handler = SIG_IGN 的深度解析与应用实践
  • day14 - html5
  • 2025年TOP5服装类跟单软件推荐榜单
  • 复杂正则语句(表格数据)解析
  • CentOS7运行AppImage
  • 历史数据分析——首旅酒店
  • 电子电气架构 --- 48V车载供电架构
  • ubuntu修改密码
  • 基于dynamic的Druid 与 HikariCP 连接池集成配置区别
  • 论文阅读 2025-8-3 [FaceXformer, RadGPT , Uni-CoT]
  • 数论——约数之和、快速乘