c++11多线程 互斥量
2016-07-13 19:40
387 查看
下面有一段两个线程同时输出的代码
运行结果:
由结果第一行我们发现 一条语句中(cout<<""<<i<<endl)
字符串 数字 换行符不是连续一起输出的,这个问题出现的原因是存在两个线程同时竞争cout这一个资源.
1 加入互斥量改进后
运行结果
这样就保证了每一条语句都是按顺序输出的
但上例代码仍存在两个问题:
1 如果邻接区出现异常,那么mu将一直保持lock的状态
2 该代码仅保证了main线程与t1线程对临界区的互斥访问,而cout这一资源可以被全局访问,仍会在其他地方出现竞争cout资源的冲突
2进一步改进:
问题1的解决方法之一是引入lock_guard
lock_guard 对象通常用于管理某个锁(Lock)对象,因此与 Mutex RAII 相关,方便线程对互斥量上锁,即在某个 lock_guard 对象的声明周期内,它所管理的锁对象会一直保持上锁状态;而 lock_guard 的生命周期结束之后,它所管理的锁对象会被解锁(注:类似
shared_ptr 等智能指针管理动态分配的内存资源 )。
在
lock_guard 对象构造时,传入的 Mutex 对象(即它所管理的 Mutex 对象)会被当前线程锁住。在lock_guard 对象被析构时,它所管理的 Mutex 对象会自动解锁,由于不需要程序员手动调用 lock 和 unlock 对 Mutex 进行上锁和解锁操作,因此这也是最简单安全的上锁和解锁方式,尤其是在程序抛出异常后先前已被上锁的 Mutex 对象可以正确进行解锁操作,极大地简化了程序员编写与 Mutex 相关的异常处理代码。
问题2 则需要我们对某一输出资源进行封装(不能是cout)
进一步改进后代码:
3 关于unique_lock
lock_guard 最大的缺点也是简单,没有给程序员提供足够的灵活度,因此,C++11 标准中定义了另外一个与 Mutex RAII 相关类 unique_lock,该类与 lock_guard 类相似,也很方便线程对互斥量上锁,但它提供了更好的上锁和解锁控制。
顾名思义,unique_lock 对象以独占所有权的方式( unique owership)管理 mutex 对象的上锁和解锁操作,所谓独占所有权,就是没有其他的 unique_lock 对象同时拥有某个 mutex 对象的所有权。
在构造(或移动(move)赋值)时,unique_lock 对象需要传递一个 Mutex 对象作为它的参数,新创建的 unique_lock 对象负责传入的 Mutex 对象的上锁和解锁操作。
std::unique_lock 对象也能保证在其自身析构时它所管理的 Mutex 对象能够被正确地解锁(即使没有显式地调用 unlock 函数)。因此,和 lock_guard 一样,这也是一种简单而又安全的上锁和解锁方式,尤其是在程序抛出异常后先前已被上锁的 Mutex 对象可以正确进行解锁操作,极大地简化了程序员编写与 Mutex 相关的异常处理代码。
#include<iostream> #include<thread> #include<string> using namespace std; void func(){ for(int i=0;i<10;i++) cout<<"in func:"<<i<<endl; } int main(){ thread t1(func); t1.detach(); for(int i=0;i<10;i++) cout<<"in main:"<<i<<endl; system("pause"); return 0; }
运行结果:
由结果第一行我们发现 一条语句中(cout<<""<<i<<endl)
字符串 数字 换行符不是连续一起输出的,这个问题出现的原因是存在两个线程同时竞争cout这一个资源.
1 加入互斥量改进后
#include<iostream> #include<thread> #include<string> #include<mutex> using namespace std; mutex mu; // std::mutex 是C++11 中最基本的互斥量,std::mutex 对象提供了独占所有权的特性 void shared_print(const string& msg,int i){ mu.lock(); cout<<msg << i <<endl; //临界区 mu.unlock(); } void func(){ for(int i=0;i<10;i++) shared_print("in func:",i); } int main(){ thread t1(func); t1.detach(); for(int i=0;i<10;i++) shared_print("in main:",i); return 0; }
运行结果
这样就保证了每一条语句都是按顺序输出的
但上例代码仍存在两个问题:
1 如果邻接区出现异常,那么mu将一直保持lock的状态
2 该代码仅保证了main线程与t1线程对临界区的互斥访问,而cout这一资源可以被全局访问,仍会在其他地方出现竞争cout资源的冲突
2进一步改进:
问题1的解决方法之一是引入lock_guard
lock_guard 对象通常用于管理某个锁(Lock)对象,因此与 Mutex RAII 相关,方便线程对互斥量上锁,即在某个 lock_guard 对象的声明周期内,它所管理的锁对象会一直保持上锁状态;而 lock_guard 的生命周期结束之后,它所管理的锁对象会被解锁(注:类似
shared_ptr 等智能指针管理动态分配的内存资源 )。
在
lock_guard 对象构造时,传入的 Mutex 对象(即它所管理的 Mutex 对象)会被当前线程锁住。在lock_guard 对象被析构时,它所管理的 Mutex 对象会自动解锁,由于不需要程序员手动调用 lock 和 unlock 对 Mutex 进行上锁和解锁操作,因此这也是最简单安全的上锁和解锁方式,尤其是在程序抛出异常后先前已被上锁的 Mutex 对象可以正确进行解锁操作,极大地简化了程序员编写与 Mutex 相关的异常处理代码。
问题2 则需要我们对某一输出资源进行封装(不能是cout)
进一步改进后代码:
#include<iostream> #include<thread> #include<string> #include<mutex> #include<fstream> using namespace std; class Log{ public: Log(){ of.open("out.txt"); } void shared_print(const string& msg,int i){ lock_guard<mutex>lockg(mu); of<<msg << i <<endl; //临界区 } private: //将输出资源与互斥量私有化 ofstream of; mutex mu; }; void func(Log& log){ for(int i=0;i<10;i++) log.shared_print("in func:",i); } int main(){ Log log; thread t1(func,ref(log)); t1.detach(); for(int i=0;i<10;i++) log.shared_print("in main:",i); return 0; }
3 关于unique_lock
lock_guard 最大的缺点也是简单,没有给程序员提供足够的灵活度,因此,C++11 标准中定义了另外一个与 Mutex RAII 相关类 unique_lock,该类与 lock_guard 类相似,也很方便线程对互斥量上锁,但它提供了更好的上锁和解锁控制。
顾名思义,unique_lock 对象以独占所有权的方式( unique owership)管理 mutex 对象的上锁和解锁操作,所谓独占所有权,就是没有其他的 unique_lock 对象同时拥有某个 mutex 对象的所有权。
在构造(或移动(move)赋值)时,unique_lock 对象需要传递一个 Mutex 对象作为它的参数,新创建的 unique_lock 对象负责传入的 Mutex 对象的上锁和解锁操作。
std::unique_lock 对象也能保证在其自身析构时它所管理的 Mutex 对象能够被正确地解锁(即使没有显式地调用 unlock 函数)。因此,和 lock_guard 一样,这也是一种简单而又安全的上锁和解锁方式,尤其是在程序抛出异常后先前已被上锁的 Mutex 对象可以正确进行解锁操作,极大地简化了程序员编写与 Mutex 相关的异常处理代码。
#include<iostream> #include<thread> #include<string> #include<mutex> #include<fstream> using namespace std; class Log{ public: Log(){ of.open("out.txt"); } void shared_print(const string& msg,int i){ unique_lock<mutex>ulock(mu,defer_lock); //std::defer_lock 申明mu的状态是未锁的 // ..... ulock.lock(); of<<msg << i <<endl; //临界区 ulock.unlock(); //....... unique_lock<mutex>ulock2 =move(ulock); //unique_lock支持移动操作 } private: ofstream of; mutex mu; }; void func(Log& log){ for(int i=0;i<10;i++) log.shared_print("in func:",i); } int main(){ Log log; thread t1(func,ref(log)); t1.detach(); for(int i=0;i<10;i++) log.shared_print("in main:",i); return 0; }
相关文章推荐
- C++ 创建动态二维数组
- C/C++语言参数传递----值传递、引用传递、指针传递、指针引用传递
- java的数据类型与c语言的区别
- 图解VC++版PE文件解析器源码分析
- 136. Single Number
- C++11 long long使用及输出
- boost静态链接库和c++/clr不兼容问题:未能加载文件或程序集,不是有效的Win32应用程序。
- LeetCode:Implement strStr()
- C++中的对齐方式及sizeof小结
- C/C++语言中#的神奇作用:把宏参数字符串化/贴合宏参数
- UE4学习笔记(三): 为什么使用C++替代UnrealScript?
- 收集的C/C++执行外部程序(调用外部exe程序)文章
- stringstream string转换成long型遇到的问题
- Leetcode 231. Power of Two (Easy) (cpp)
- 浅谈C语言中"#ifndef/#define/#endif"的作用
- (C++)UNION结构
- 常用算法题目3道
- 浅析长度为0的数组
- OpenSSLRSA.cpp
- 指针小解