c++并发编程之互斥锁(mutex)的使用方法
1. 多个线程访问同一资源时,为了保证数据的一致性,最简单的方式就是使用 mutex(互斥锁)。
引用 cppreference 的介绍:
The mutex class is a synchronization primitive that can be used to protect shared data from being simultaneously accessed by multiple threads.
方法1:直接操作 mutex,即直接调用 mutex 的 lock / unlock 函数
此例顺带使用了 boost::thread_group 来创建一组线程。
#include <iostream>
#include <boost/thread/mutex.hpp>
#include <boost/thread/thread.hpp> boost::mutex mutex;
int count = ; void Counter() {
mutex.lock(); int i = ++count;
std::cout << "count == " << i << std::endl; // 前面代码如有异常,unlock 就调不到了。
mutex.unlock();
} int main() {
// 创建一组线程。
boost::thread_group threads;
for (int i = ; i < ; ++i) {
threads.create_thread(&Counter);
} // 等待所有线程结束。
threads.join_all();
return ;
}
方法2:使用 lock_guard 自动加锁、解锁。原理是 RAII,和智能指针类似
#include <iostream>
#include <boost/thread/lock_guard.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/thread.hpp> boost::mutex mutex;
int count = ; void Counter() {
// lock_guard 在构造函数里加锁,在析构函数里解锁。
boost::lock_guard<boost::mutex> lock(mutex); int i = ++count;
std::cout << "count == " << i << std::endl;
} int main() {
boost::thread_group threads;
for (int i = ; i < ; ++i) {
threads.create_thread(&Counter);
} threads.join_all();
return ;
}
方法3:使用 unique_lock 自动加锁、解锁unique_lock 与 lock_guard 原理相同,但是提供了更多功能(比如可以结合条件变量使用)。
注意:mutex::scoped_lock 其实就是 unique_lock<mutex> 的 typedef。
#include <iostream>
#include <boost/thread/mutex.hpp>
#include <boost/thread/thread.hpp> boost::mutex mutex;
int count = ; void Counter() {
boost::unique_lock<boost::mutex> lock(mutex); int i = ++count;
std::cout << "count == " << i << std::endl;
} int main() {
boost::thread_group threads;
for (int i = ; i < ; ++i) {
threads.create_thread(&Counter);
} threads.join_all();
return ;
}
方法4:为输出流使用单独的 mutex
这么做是因为 IO 流并不是线程安全的!
如果不对 IO 进行同步,此例的输出很可能变成:
count == count == 2count ==
count ==
因为在下面这条输出语句中:
std::cout << "count == " << i << std::endl;
输出 "count == " 和 i 这两个动作不是原子性的(atomic),可能被其他线程打断。
#include <iostream>
#include <boost/thread/mutex.hpp>
#include <boost/thread/thread.hpp>
#include <boost/thread/lock_guard.hpp> boost::mutex mutex;
boost::mutex io_mutex;
int count = ; void Counter() {
int i;
{
boost::unique_lock<boost::mutex> lock(mutex);
i = ++count;
} {
boost::unique_lock<boost::mutex> lock(io_mutex);
std::cout << "count == " << i << std::endl;
}
} int main() {
boost::thread_group threads;
for (int i = ; i < ; ++i) {
threads.create_thread(&Counter);
} threads.join_all();
return ;
}
2. 保护共享数据的替代设施
2.1 保护共享数据的初始化过程
丑陋的代码:
void undefined_behaviour_with_double_checked_locking()
{
if(!resource_ptr) //
{
std::lock_guard<std::mutex> lk(resource_mutex);
if(!resource_ptr) //
{
resource_ptr.reset(new some_resource); //
}
}
resource_ptr->do_something(); //
}
这个模式为什么声名狼藉呢?因为这里有潜在的条件竞争,因为外部的读取锁①没有与内部的
写入锁进行同步③。因此就会产生条件竞争,这个条件竞争不仅覆盖指针本身,还会影响到其
指向的对象;即使一个线程知道另一个线程完成对指针进行写入,它可能没有看到新创建的
some_resource实例,然后调用do_something()④后,得到不正确的结果。
C++标准库提供了 std::once_flag 和 std::call_once 来处理这种情况。比起锁住互斥量,并显式的检查指
针,每个线程只需要使用 std::call_once ,在 std::call_once 的结束时,就能安全的知道指
针已经被其他的线程初始化了。使用 std::call_once 比显式使用互斥量消耗的资源更少,特
别是当初始化完成后。
std::shared_ptr<some_resource> resource_ptr;
std::once_flag resource_flag; //
void init_resource()
{
resource_ptr.reset(new some_resource);
}
void foo()
{
std::call_once(resource_flag,init_resource); // 可以完整的进行一次初始化
resource_ptr->do_something();
}
2.2 保护很少更新的数据结构
虽然更新频度很低,但更新也是有可能发生的,并且当这个可缓存被多个线程访问,这个缓
存就需要适当的保护措施,来对其处于更新状态时进行保护,也为了确保线程读到缓存中的
有效数据。
使用一
个 std::mutex 来保护数据结构,这的确有些反应过度,因为在没有发生修改时,它将削减并
发读取数据的可能性;这里需要另一种不同的互斥量。这种新的互斥量常被称为“读者-写者
锁”(reader-writer mutex),因为其允许两中不同的使用方式:一个“作者”线程独占访问和共
享访问,让多个“读者”线程并发访问。
新的C++标准库应该不提供这样的互斥量,Boost库提供了boost::shared_mutex。
3.3 嵌套锁
C++标准库提供了 std::recursive_mutex 类。其功能与 std::mutex 类似,除了你可以从
同一线程的单个实例上获取多个锁。在互斥量锁住其他线程前,你必须释放你拥有的所有
锁,所以当你调用lock()三次时,你也必须调用unlock()三次。正确使
用 std::lock_guard<std::recursive_mutex> 和 std::unique_lock<std::recursice_mutex> 可以帮
你处理这些问题。
c++并发编程之互斥锁(mutex)的使用方法的更多相关文章
- python 并发编程 多进程 互斥锁 目录
python 并发编程 多进程 互斥锁 模拟抢票 互斥锁与join区别
- python 并发编程 多进程 互斥锁
运行多进程 每个子进程的内存空间是互相隔离的 进程之间数据不能共享的 一 互斥锁 但是进程之间都是运行在一个操作系统上,进程之间数据不共享,但是共享同一套文件系统,所以访问同一个文件,或同一个打印终 ...
- C++ 并发编程之互斥锁和条件变量的性能比较
介绍 本文以最简单生产者消费者模型,通过运行程序,观察该进程的cpu使用率,来对比使用互斥锁 和 互斥锁+条件变量的性能比较. 本例子的生产者消费者模型,1个生产者,5个消费者. 生产者线程往队列里放 ...
- python 并发编程 多线程 互斥锁
互斥锁 并行变成串行,牺牲效率 保证数据安全,实现局部串行 保护不同的数据,应该加不同的锁 现在一个进程 可以有多个线程 所有线程都共享进程的地址空间 实现数据共享 共享带来问题就会出现竞争 竞争就会 ...
- python 并发编程 多进程 互斥锁与join区别
互斥锁与join 互斥锁和join都可以把并发变成串行 以下代码是用join实现串行 from multiprocessing import Process import time import js ...
- 并发编程 Process 互斥锁
进程理论 程序与进程的区别 ''' 程序不是存在硬盘上的代码,相对来说是静态的 进程表示程序在执行的过程,是动态的 ''' 进程的调度 先来先服务调度算法 '''对长作业有利,对短作业无益''' 短作 ...
- 互斥锁Mutex与信号量Semaphore的区别
转自互斥锁Mutex与信号量Semaphore的区别 多线程编程中,常常会遇到这两个概念:Mutex和Semaphore,两者之间区别如下: 有人做过如下类比: Mutex是一把钥匙,一个人拿了就可进 ...
- Golang之并发资源竞争(互斥锁)
并发本身并不复杂,但是因为有了资源竞争的问题,就使得我们开发出好的并发程序变得复杂起来,因为会引起很多莫名其妙的问题. package main import ( "fmt" &q ...
- 线程锁(互斥锁Mutex)
线程锁(互斥锁Mutex) 一个进程下可以启动多个线程,多个线程共享父进程的内存空间,也就意味着每个线程可以访问同一份数据,此时,如果2个线程同时要修改同一份数据,会出现什么状况? # -*- cod ...
随机推荐
- 20155328 网络攻防 实验五:MSF基础应用
20155328 网络攻防 实验五:MSF基础应用 实践内容及过程记录 一.Windows服务渗透攻击----ms08_067 攻击机:kali 靶机:WindowsXP(英文版) 第一步,分别查看攻 ...
- MAC下配置ssh让SourceTree通过秘钥访问远程仓库
问题描述 由于TortoiseGit没有MAC版本,我们使用了SourceTree来替代. 在帮同事解决Mac下的Git的时候,碰到一个问题:SourceTree无法使用ssh方式提交代码,这是由于没 ...
- SCC的奇葩算法——Kosaraju
不会Tarjan,难道就不能与邪恶的SCC作斗争了吗? 祭出Kosaraju. 一些变量名的意义: a[N] 原图的vector存储 b[N] 原图的所有边反向vector存储 s dfs得出的拓扑序 ...
- 汇编 inc 和 dec 指令
知识点: inc 加1指令 dec 减1指令 一.加一指令inc inc a 相当于 add a, //i++ 优点 速度比sub指令快,占用空间小 这条指令执行结果影响AF.OF.PF.SF.Z ...
- 为什么要进行阿里云云计算助理工程师认证(ACA)
阿里云助理工程师认证(ACA - Alibaba Cloud Certification Associate)是面向使用阿里云基础产品的专业技术认证,主要涉及阿里云的计算.存储.网络.安全类的核心产品 ...
- GAN初步——本质上就是在做优化,对于生成器传给辨别器的生成图片,生成器希望辨别器打上标签 1,体现在loss上!
from:https://www.sohu.com/a/159976204_717210 GAN 从 2014 年诞生以来发展的是相当火热,比较著名的 GAN 的应用有 Pix2Pix.CycleGA ...
- Docker 部署学习
https://yeasy.gitbooks.io/docker_practice/basic_concept/repository.html https://hujb2000.gitbooks.io ...
- Windows10没有修改hosts文件权限的解决方案(亲测有效)
当遇到有hosts文件不会编辑或者,修改了没办法保存”,以及需要权限等问题如图: 或者这样: 我学了一招,现在教给你: 1.win+R 2.进入hosts的文件所在目录: 3.我们开始如何操作才能不出 ...
- 在windows10上搭建caffe
caffe环境的搭建一直是让我最头疼的,最近在Windows10上成功搭建了caffe,在此对搭建过程进行记录. 安装主要是按照caffe github上的安装说明进行的,caffe的github主页 ...
- 第一个spring冲刺总结及后诸葛亮报告(附团队贡献分)
眨眼就完结了第一阶段的冲刺了,之前因为学校停电停水等诸多原因而导致冲刺完毕时间的推迟. 第一阶段总体是做到了运算的功能,只是一些基本的功能实现,但能保证的容错性能较高. 1.在普遍的四则运算中都能见到 ...