C++多线程编程中通常会对共享的数据进行写保护,以防止多线程在对共享数据成员进行读写时造成资源争抢导致程序出现未定义的行为。通常的做法是在修改共享数据成员的时候进行加锁--mutex。在使用锁的时候通常是在对共享数据进行修改之前进行lock操作,在写完之后再进行unlock操作,进场会出现由于疏忽导致由于lock之后在离开共享成员操作区域时忘记unlock,导致死锁。

针对以上的问题,C++11中引入了std::unique_lock与std::lock_guard两种数据结构。通过对lock和unlock进行一次薄的封装,实现自动unlock的功能。

 std::mutex mut;

 void insert_data()
{
std::lock_guard<std::mutex> lk(mut);
queue.push_back(data);
} void process_data()
{
std::unqiue_lock<std::mutex> lk(mut);
queue.pop();
}

std::unique_lock 与std::lock_guard都能实现自动加锁与解锁功能,但是std::unique_lock要比std::lock_guard更灵活,但是更灵活的代价是占用空间相对更大一点且相对更慢一点。

1 回顾采用RAII手法管理mutex的std::lock_guard其功能是在对象构造时将mutex加锁,析构时对mutex解锁,这样一个栈对象保证了在异常情形下mutex可以在lock_guard对象析构被解锁,lock_guard拥有mutex的所有权。

 explicit lock_guard (mutex_type& m);//必须要传递一个mutex作为构造参数
lock_guard (mutex_type& m, adopt_lock_t tag);//tag=adopt_lock表示mutex已经在之前被上锁,这里lock_guard将拥有mutex的所有权
lock_guard (const lock_guard&) = delete;//不允许copy constructor

2 再来看一个与std::lock_guard功能相似但功能更加灵活的管理mutex的对象 std::unique_lock,unique_lock内部持有mutex的状态:locked,unlocked。unique_lock比lock_guard占用空间和速度慢一些,因为其要维护mutex的状态。

  unique_lock() noexcept;    //可以构造一个空的unique_lock对象,此时并不拥有任何mutex

  explicit unique_lock (mutex_type& m);//拥有mutex,并调用mutex.lock()对其上锁    

  unique_lock (mutex_type& m, try_to_lock_t tag);//tag=try_lock表示调用mutex.try_lock()尝试加锁

  unique_lock (mutex_type& m, defer_lock_t tag) noexcept;//tag=defer_lock表示不对mutex加锁,只管理mutex,此时mutex应该是没有加锁的

  unique_lock (mutex_type& m, adopt_lock_t tag);//tag=adopt_lock表示mutex在此之前已经被上锁,此时unique_locl管理mutex

  template <class Rep, class Period>
unique_lock (mutex_type& m, const chrono::duration<Rep,Period>& rel_time);//在一段时间rel_time内尝试对mutex加锁,mutex.try_lock_for(rel_time) template <class Clock, class Duration>
unique_lock (mutex_type& m, const chrono::time_point<Clock,Duration>& abs_time);//mutex.try_lock_until(abs_time)直到abs_time尝试加锁 unique_lock (const unique_lock&) = delete;//禁止拷贝构造 unique_lock (unique_lock&& x);//获得x管理的mutex,此后x不再和mutex相关,x此后相当于一个默认构造的unique_lock,移动构造函数,具备移动语义,movable but not copyable

说明:其中2和5拥有mutex的所有权,而1和4永远不用有mutex的所有权,3和6及7若尝试加锁成功则拥有mutex的所有权

unique_lock 在使用上比lock_guard更具有弹性,和 lock_guard 相比,unique_lock 主要的特色在于:
         unique_lock 不一定要拥有 mutex,所以可以透过 default constructor 建立出一个空的 unique_lock。
         unique_lock 虽然一样不可复制(non-copyable),但是它是可以转移的(movable)。所以,unique_lock 不但可以被函数回传,也可以放到 STL 的 container 里。
         另外,unique_lock 也有提供 lock()、unlock() 等函数,可以用来加锁解锁mutex,也算是功能比较完整的地方。
         unique_lock本身还可以用于std::lock参数,因为其具备lock、unlock、try_lock成员函数,这些函数不仅完成针对mutex的操作还要更新mutex的状态。

3  std::unique_lock其它成员函数

 ~unique_lock();//若unique_lock对象拥有管理的mutex的所有权,mutex没有被销毁或者unlock,那么将执行mutex::unlock()解锁,并不销毁mutex对象。
mutex_type* mutex() const noexcept;//返回unique_lock管理的mutex指针,但是unique_lock不会放弃对mutex的管理,若unique_lock对mutex上锁了,其有义务对mutex解锁
bool owns_lock() const noexcept;//当mutex被unique_lock上锁,且mutex没有解锁或析构,返回真,否则返回false
explicit operator bool() const noexcept;//同上

4  std::unique_lock增加了灵活性,比如可以对mutex的管理从一个scope通过move语义转到另一个scope,不像lock_guard只能在一个scope中生存。同时也增加了管理的难度,因此如无必要还是用lock_guard。

5 网上看见一个unique_lock的应用于银行转账的实例,贴在这里:

 #include <mutex>
#include <thread>
#include <chrono>
#include <iostream>
#include <string>
using namespace std;
struct bank_account//银行账户
{
explicit bank_account(string name, int money)
{
sName = name;
iMoney = money;
} string sName;
int iMoney;
mutex mMutex;//账户都有一个锁mutex
};
void transfer( bank_account &from, bank_account &to, int amount )//这里缺少一个from==to的条件判断个人觉得
{
unique_lock<mutex> lock1( from.mMutex, defer_lock );//defer_lock表示延迟加锁,此处只管理mutex
unique_lock<mutex> lock2( to.mMutex, defer_lock );
lock( lock1, lock2 );//lock一次性锁住多个mutex防止deadlock
from.iMoney -= amount;
to.iMoney += amount;
cout << "Transfer " << amount << " from "<< from.sName << " to " << to.sName << endl;
}
int main()
{
bank_account Account1( "User1", );
bank_account Account2( "User2", );
thread t1( [&](){ transfer( Account1, Account2, ); } );//lambda表达式
thread t2( [&](){ transfer( Account2, Account1, ); } );
t1.join();
t2.join();
}

说明:加锁的时候为什么不是如下这样的?在前面一篇博文中有讲到多个语句加锁可能导致deadlock,假设:同一时刻A向B转账,B也向A转账,那么先持有自己的锁再相互请求对方的锁必然deadlock。

 lock_guard<mutex> lock1( from.mMutex );
lock_guard<mutex> lock2( to.mMutex );

采用lock_guard也可以如下:

 lock( from.mMutex, to.mMutex );
lock_guard<mutex> lock1( from.mMutex, adopt_lock );//adopt_lock表示mutex已经上锁,lock1将拥有from.mMutex
lock_guard<mutex> lock2( to.mMutex, adopt_lock );

6 上面的例子lock针对mutex加锁后,并没有显示解锁,那么离开lock的作用域后解锁了吗?验证代码如下,在lock后抛出异常mutex解锁了吗?:

 #include<mutex>
#include<exception>
#include<iostream>
using namespace std;
int main(){
mutex one,two;
try{
{
lock(one,two);
throw ;
cout<<"locking..."<<endl;
}
}catch(int){
cout<<"catch..."<<endl;
}
if(!one.try_lock()&&!two.try_lock())
cout<<"failure"<<endl;
else
cout<<"success"<<endl;
return ;
}

程序输出:

catch...
success          //lock后的操作抛出异常后,mutex解锁了

7 unique_lock is movable but not copyable.因此可以作为函数返回值,STL容器元素。例如:一个函数采用unique_lock加锁mutex然后准备好数据并将unique_lock返回给调用者,调用者在mutex保护下对数据进一步加工,简单的代码如下:

 #include<mutex>
#include<iostream>
using namespace std;
mutex m;
unique_lock<mutex> get_lock(){
unique_lock<mutex> lk(m);
cout<<"prepare data..."<<endl;//准备数据
return lk;//移动构造
}
int main(){
unique_lock<mutex> lk(get_lock());
cout<<"process data..."<<endl;//在mutex保护下数据深加工
return ;
}

8 unique_lock::lock(), unique_lock::unlock()这一组成员函数充分说明了,unique_lock在构造时不必对mutex加锁且可以在后期某个时候对mutex加锁; unique_lock可以在自己实例销毁前调用unique_lock::unlock()提前释放锁,这对于一些分支语句中可能得到性能提升。

C++11 std::unique_lock与std::lock_guard区别及多线程应用实例的更多相关文章

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

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

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

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

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

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

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

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

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

  6. std::lock_guard/std::unique_lock

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

  7. 基于std::mutex std::lock_guard std::condition_variable 和std::async实现的简单同步队列

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

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

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

  9. C++11 并发指南五(std::condition_variable 详解)

    前面三讲<C++11 并发指南二(std::thread 详解)>,<C++11 并发指南三(std::mutex 详解)>分别介绍了 std::thread,std::mut ...

随机推荐

  1. Java NIO -2

    NIO http://www.cnblogs.com/puyangsky/p/5840873.html -- 操作系统与 Java 基于流的 I/O模型有些不匹配.操作系统要移动的是大块数据(缓冲区) ...

  2. 使用Maven搭建Struts2框架的开发环境

    一.创建基于Maven的Web项目

  3. C#开发Unity游戏教程之Unity中方法的参数

    C#开发Unity游戏教程之Unity中方法的参数 Unity的方法的参数 出现在脚本中的方法,无论是在定义的时候,还是使用的时候,后面都跟着一对括号“( )”,有意义吗?看起来最多也就是起个快速识别 ...

  4. codevs 2804 最大最小数质因数

    题目描述 Description 先输入n,n<=20;再依次输入n个不同的数,每个数<=1000000;找出最大数和最小数,并输出最大最小数的质因数,没有质因数则输出0. 输入描述 In ...

  5. [java] 虚拟机(JVM)底层结构详解[转]

    本文来自:曹胜欢博客专栏.转载请注明出处:http://blog.csdn.net/csh624366188 在以前的博客里面,我们介绍了在java领域中大部分的知识点,从最基础的java最基本语法到 ...

  6. Qt编写websocketpp客户端

    1.下载websocketpp,地址为https://github.com/zaphoyd/websocketpp,版本为0.7. 2.下载boost,地址为https://www.boost.org ...

  7. Azure虚机磁盘容量警报(邮件提醒)

    上周有个客户提出这样的需求:根据虚拟机磁盘的实际使用量,当达到某一阈值时设置邮件提醒. 在这个需求中我们只需要解决两点问题: 计算虚拟机磁盘实际使用量 发送邮件 使用VS新建一个名为Calculate ...

  8. MikroTik RouterOS 5.x使用HunterTik 2.3.1进行破解

    一.加载光驱: 二.一路回车: 三.说明: 1.可以不安装Debian内核,但如果在无缝升级到6.6的版本,此项就一定要选择. 2.6版本的破解必须小于等于1G的空间,不然无法破解成功,亲测有效,如果 ...

  9. Android ListView理解之BaseAdapter

    ListView是Android开发过程中较为常见的组件之一,它将数据以列表的形式展现出来.一般而言,一个ListView由以下三个元素组 成: 1.View,用于展示列表,通常是一个xml所指定的. ...

  10. SAE J1850 VPW Implement

    ---恢复内容开始--- OBDII Interface Project When I can ever find enough time away from schoolwork, I try to ...