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::lockstd::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区别的更多相关文章

  1. C++ 并发编程,std::unique_lock与std::lock_guard区别示例

    背景 平时看代码时,也会使用到std::lock_guard,但是std::unique_lock用的比较少.在看并发编程,这里总结一下.方便后续使用. std::unique_lock也可以提供自动 ...

  2. C++11 std::unique_lock与std::lock_guard区别及多线程应用实例

    C++多线程编程中通常会对共享的数据进行写保护,以防止多线程在对共享数据成员进行读写时造成资源争抢导致程序出现未定义的行为.通常的做法是在修改共享数据成员的时候进行加锁--mutex.在使用锁的时候通 ...

  3. std::unique_lock与std::lock_guard区别示例

    std::lock_guard std::lock_guard<std::mutex> lk(frame_mutex); std::unique_lock<std::mutex> ...

  4. std::unique_lock与std::lock_guard分析

    背景 C++多线程编程中通常会对共享的数据进行写保护,以防止多线程在对共享数据成员进行读写时造成资源争抢,导致程序出现未定义或异常行为.通常的做法是在修改共享数据成员时进行加锁(mutex).在使用锁 ...

  5. C++ 11 多线程下std::unique_lock与std::lock_guard的区别和用法

    这里主要介绍std::unique_lock与std::lock_guard的区别用法 先说简单的 一.std::lock_guard的用法 std::lock_guard其实就是简单的RAII封装, ...

  6. 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 ...

  7. boost::unique_lock和boost::lock_guard的区别

    lock_guard unique_lock boost::mutex mutex; boost::unique_lock<boost::mutex> lock(mutex); std:: ...

  8. C++11中lock_guard和unique_lock的区别

    c++11中有一个区域锁lock_guard,还有第二个区域锁unique_lock. 区域锁lock_guard使用起来比较简单,除了构造函数外没有其他member function,在整个区域都有 ...

  9. 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 ...

随机推荐

  1. UE4 UnLoadStreamLevel

    今天测试发现一个bug记录一下,如果把某一个子关卡的加载方式设置为Always Loaded时,调用UnloadStreamLevel时不会执行Completed后面的节点,也就是一直没有完成.

  2. CSS3技巧巧妙使用:not(:last-of-type)简化你的css代码

    终于找到了一个好方法,使用:not(:last-of-type)简单方便,再也不要麻烦的单独使用:last-of-type了,不错! 应用场景:平时我们的列表一般都会有分割线,但是最后一个列表没有分割 ...

  3. k8s 创建资源的两种方式 - 每天5分钟玩转 Docker 容器技术(124)

    命令 vs 配置文件 Kubernetes 支持两种方式创建资源: 1. 用 kubectl 命令直接创建,比如: kubectl run nginx-deployment --image=nginx ...

  4. php网站在服务器上邮件发送不了,在本地可以

    标签: php邮箱 2015-11-27 13:58 879人阅读 评论(0) 收藏 举报 分类: php(2) 版权声明:本文为博主原创文章,未经博主允许不得转载. 最近在做phpmailer发送邮 ...

  5. Oracle创建、管理撤销表空间

    撤销管理模式: 用户通过设定撤销管理模式(undo mode)就可以灵活地选择使用手动撤销管理(manual undo management)或自动撤销管理(automatic undo manage ...

  6. RequireJS(一)

    RequireJS: RequireJS中文网:http://www.requirejs.cn/ 解决HTML引入大量js文件导致的问题: 首先是加载的时候,浏览器会停止网页渲染,加载文件越多,网页失 ...

  7. Java - 路线图

    java语言基础 基本语法 面向对象思想 mysql数据库基础 jdbc操作 java高级技术 java集合框架 多线程 网络编程 sql深入,索引,sql优化 javaweb servlet jsp ...

  8. python3 第十章 - 如何进行进制转化

    在计算机的世界里,2进制是主流,而在人类的自然世界中,10进制是主流,那么在这之间必然就会存在进制转化的问题.本章我们就来谈谈进制转化,也希望通过本章加深您对前些章所学知识的理解. 原理:先说说关于位 ...

  9. CentOS7.x机器安装Azure CLI2.0

    安装Azure CLI 2.0的前提是:机器中必须有 Python 2.7.x 或 Python 3.x.如果机器中没有其中任何一个Python的版本,请及时安装 1.准备一台CentOS 7.3的机 ...

  10. Django 模版中如何对主菜单进行选中?

    问题描叙: 在实际模版中,返回的页面我们想要主菜单保持一种状态,这个主菜单进去的子页面,都需要保持主菜单选择状态 例如 主菜单是 课程, 下面展现的章节和视频页面时候, 课程这个主菜单在展现这个菜单下 ...