scope_lock与lock_guard区别
lock_guard:更加灵活的锁管理类模板,构造时是否加锁是可选的,在对象析构时如果持有锁会自动释放锁,所有权可以转移。对象生命期内允许手动加锁和释放锁。
scope_lock:严格基于作用域(scope-based)的锁管理类模板,构造时是否加锁是可选的(不加锁时假定当前线程已经获得锁的所有权),析构时自动释放锁,所有权不可转移,对象生存期内不允许手动加锁和释放锁。
share_lock:用于管理可转移和共享所有权的互斥对象。
使用std::lock_guard类模板修改前面的代码,在lck对象构造时加锁,析构时自动释放锁,即使insert抛出了异常lck对象也会被正确的析构,所以也就不会发生互斥对象没有释放锁而导致死锁的问题。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
std::set<int> int_set;std::mutex mt;auto f = [&int_set, &mt]() { try { std::random_device rd; std::mt19937 gen(rd()); std::uniform_int_distribution<> dis(1, 1000); for(std::size_t i = 0; i != 100000; ++i) { std::lock_guard<std::mutex> lck(mt); int_set.insert(dis(gen)); } } catch(...) {}};std::thread td1(f), td2(f);td1.join();td2.join(); |
互斥对象管理类模板的加锁策略
前面提到std::lock_guard、std::unique_lock和std::shared_lock类模板在构造时是否加锁是可选的,C++11提供了3种加锁策略。
| 策略 | tag type | 描述 |
|---|---|---|
| (默认) | 无 | 请求锁,阻塞当前线程直到成功获得锁。 |
| std::defer_lock | std::defer_lock_t | 不请求锁。 |
| std::try_to_lock | std::try_to_lock_t | 尝试请求锁,但不阻塞线程,锁不可用时也会立即返回。 |
| std::adopt_lock | std::adopt_lock_t | 假定当前线程已经获得互斥对象的所有权,所以不再请求锁。 |
下表列出了互斥对象管理类模板对各策略的支持情况。
| 策略 | std::lock_guard | std::unique_lock | std::shared_lock |
|---|---|---|---|
| (默认) | √ | √ | √(共享) |
| std::defer_lock | × | √ | √ |
| std::try_to_lock | × | √ | √ |
| std::adopt_lock | √ | √ | √ |
下面的代码中std::unique_lock指定了std::defer_lock。
|
1
2
3
4
5
|
std::mutex mt;std::unique_lock<std::mutex> lck(mt, std::defer_lock);assert(lck.owns_lock() == false);lck.lock();assert(lck.owns_lock() == true); |
对多个互斥对象加锁
在某些情况下我们可能需要对多个互斥对象进行加锁,考虑下面的代码
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
std::mutex mt1, mt2;// thread 1{ std::lock_guard<std::mutex> lck1(mt1); std::lock_guard<std::mutex> lck2(mt2); // do something}// thread 2{ std::lock_guard<std::mutex> lck2(mt2); std::lock_guard<std::mutex> lck1(mt1); // do something} |
如果线程1执行到第5行的时候恰好线程2执行到第11行。那么就会出现
- 线程1持有mt1并等待mt2
- 线程2持有mt2并等待mt1
发生死锁。 为了避免发生这类死锁,对于任意两个互斥对象,在多个线程中进行加锁时应保证其先后顺序是一致。前面的代码应修改成
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
std::mutex mt1, mt2;// thread 1{ std::lock_guard<std::mutex> lck1(mt1); std::lock_guard<std::mutex> lck2(mt2); // do something}// thread 2{ std::lock_guard<std::mutex> lck1(mt1); std::lock_guard<std::mutex> lck2(mt2); // do something} |
更好的做法是使用标准库中的std::lock和std::try_lock函数来对多个Lockable对象加锁。std::lock(或std::try_lock)会使用一种避免死锁的算法对多个待加锁对象进行lock操作(std::try_lock进行try_lock操作),当待加锁的对象中有不可用对象时std::lock会阻塞当前线程知道所有对象都可用(std::try_lock不会阻塞线程当有对象不可用时会释放已经加锁的其他对象并立即返回)。使用std::lock改写前面的代码,这里刻意让第6行和第13行的参数顺序不同
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
std::mutex mt1, mt2;// thread 1{ std::unique_lock<std::mutex> lck1(mt1, std::defer_lock); std::unique_lock<std::mutex> lck2(mt2, std::defer_lock); std::lock(lck1, lck2); // do something}// thread 2{ std::unique_lock<std::mutex> lck1(mt1, std::defer_lock); std::unique_lock<std::mutex> lck2(mt2, std::defer_lock); std::lock(lck2, lck1); // do something} |
此外std::lock和std::try_lock还是异常安全的函数(要求待加锁的对象unlock操作不允许抛出异常),当对多个对象加锁时,其中如果有某个对象在lock或try_lock时抛出异常,std::lock或std::try_lock会捕获这个异常并将之前已经加锁的对象逐个执行unlock操作,然后重新抛出这个异常(异常中立)。并且std::lock_guard的构造函数lock_guard(mutex_type& m, std::adopt_lock_t t)也不会抛出异常。所以std::lock像下面这么用也是正确
|
1
2
3
|
std::lock(mt1, mt2);std::lock_guard<std::mutex> lck1(mt1, std::adopt_lock);std::lock_guard<std::mutex> lck2(mt2, std::adopt_lock); |
scope_lock与lock_guard区别的更多相关文章
- C++ 并发编程,std::unique_lock与std::lock_guard区别示例
背景 平时看代码时,也会使用到std::lock_guard,但是std::unique_lock用的比较少.在看并发编程,这里总结一下.方便后续使用. std::unique_lock也可以提供自动 ...
- C++11 std::unique_lock与std::lock_guard区别及多线程应用实例
C++多线程编程中通常会对共享的数据进行写保护,以防止多线程在对共享数据成员进行读写时造成资源争抢导致程序出现未定义的行为.通常的做法是在修改共享数据成员的时候进行加锁--mutex.在使用锁的时候通 ...
- std::unique_lock与std::lock_guard区别示例
std::lock_guard std::lock_guard<std::mutex> lk(frame_mutex); std::unique_lock<std::mutex> ...
- std::unique_lock与std::lock_guard分析
背景 C++多线程编程中通常会对共享的数据进行写保护,以防止多线程在对共享数据成员进行读写时造成资源争抢,导致程序出现未定义或异常行为.通常的做法是在修改共享数据成员时进行加锁(mutex).在使用锁 ...
- C++ 11 多线程下std::unique_lock与std::lock_guard的区别和用法
这里主要介绍std::unique_lock与std::lock_guard的区别用法 先说简单的 一.std::lock_guard的用法 std::lock_guard其实就是简单的RAII封装, ...
- std::unique_lock<std::mutex> or std::lock_guard<std::mutex> C++11 区别
http://stackoverflow.com/questions/20516773/stdunique-lockstdmutex-or-stdlock-guardstdmutex The diff ...
- boost::unique_lock和boost::lock_guard的区别
lock_guard unique_lock boost::mutex mutex; boost::unique_lock<boost::mutex> lock(mutex); std:: ...
- C++11中lock_guard和unique_lock的区别
c++11中有一个区域锁lock_guard,还有第二个区域锁unique_lock. 区域锁lock_guard使用起来比较简单,除了构造函数外没有其他member function,在整个区域都有 ...
- std::lock_guard和std::unique_lock的区别
std::lock_guard 1 初始化的时候锁定std::mutex std::mutex m_mtx; std::lock_guard<std::mutex> m_lock(m_mt ...
随机推荐
- NYOJ 2356 哈希计划(模拟)
题目链接: http://acm.nyist.me/JudgeOnline/problem.php?id=2356 题目描述 众所周知,LLM的算法之所以菜,就是因为成天打游戏,最近LLM突然想玩&l ...
- NopCommerce用core重写ef
最近看了NopCommerce源码,用core学习着写了一个项目,修改的地方记录下.项目地址 NopCommerce框架出来好久了.18年的第一季度 懒加载出来后也会全部移动到.net core.那么 ...
- 邓_tp_笔记
<?phpnamespace app\teacher\controller;use think\Db;use app\common\model\Classcourse;use app\commo ...
- [转]另一种遍历Map的方式: Map.Entry 和 Map.entrySet()
转自: http://blog.csdn.net/mageshuai/article/details/3523116 今天看Think in java 的GUI这一章的时候,里面的TextArea这个 ...
- 让我们一起爱(装)上Homestead吧
本文是Laravel实战:任务管理系统(一)的扩展阅读原文链接 先来点残酷现实: 真正用过homestead的,一般不会问homestead到底好在哪里 如果你还没有爱上homestead,只能说明 ...
- np.array转换为list,嵌套的python list转成一个一维的python list
np.array转换为list 1 meitan = shuju.iloc[start:end, 1:2] zhengqi = shuju.iloc[start:end,2:3] print(type ...
- 【bird-java】分布式服务间的事件总线EventBus
什么是EventBusEventBus是对发布-订阅模式的一种实现.其以一种非常优雅的方式实现了组件间的解耦与通信,在Android开发.DDD等领域都有非常广泛的应用. 事件流大致如下: Produ ...
- 可等待计时器添加APC测试
可等待计时器和用户计时器的最大区别在于用户计时器需要在应用程序中使用大量的用户界面基础设施,从而消耗更多的资源.此外,可等待计时器是内核对象,这意味着他们不仅可以在多个线程间共享,而且具备安全性. 用 ...
- mysql存储过程且mybatis调用
首先,需要执行符DELIMITER ,建议用//,即在存储过程开始前定义delimiter //,在结束后加上//,最后加上DELIMITER ; 具体原因@参考文章1写的很清楚,不再赘述. 参考文章 ...
- JavaScript this浅析
在做聊天室的过程中,我遇到了一个小问题,在javascrip中,有没有和c语言中的静态变量类似的对象呢? 答案就在闭包之中. 而说到闭包,又得说说那个我视之如地雷的this.this搞明白了,闭包这东 ...