一、互斥量mutex
保护共享数据,操作时,用代码把共享数据锁住,操作数据,解锁。其他线程要操作共享数据的线程必须等待解锁,锁住,操作,解锁。
互斥量就是类对象,一个锁,多个线程用lock()成员函数加锁这个锁头,只有一个线程能锁成功,成功的标志是lock函数返回,如果没有锁成功,那么流程就卡在lock()这里,不断的尝试去锁这个锁头。
互斥量使用要小心,保护数据多了,影响效率,保护少了,没达到保护效果。
二、互斥量用法
1、lock()、unlock()
include
先lock,操作共享数据,再unlock。
使用规则:成对使用,有lock必须有unlock;
1 #include
2 #include //线程
3 #include
4 #include
5 #include
6 /*
7 网络游戏服务器8 创建两个线程,一个线程收集玩家命令(用一个数字代表玩家命令),并把命令数据写到一个队列中;9 另一个线程从队列中取出玩家发送来的命令,解析,然后执行玩家需要的动作。10 list:频繁顺序插入和删除数据时效率高;11 vector:频繁随机插入和删除数据时效率高;12 准备使用成员函数来作为线程函数13 */
14 using namespacestd;15 classA{16 public:17 //把收到的消息(玩家命令)让入到一个队列中的线程函数
18 voidInMsgQue(){19 for(int i=0;i<100;i++){20 cout<
23 my_mutex.unlock();24 }25
26 }27 bool outMsgLULProc(int &command){//这样的目的是为了方便后序对command的操作28 my_mutex.lock();29 if(!MyQue.empty()){30 int command =MyQue.front();31 MyQue.pop_front();32 my_mutex.unlock();//这个unlock特别容易被忘记写33 return true;34 }35 my_mutex.unlock();36 return false;37 }38 //把数据从消息队列中取出来的线程函数
39 voidOutMsgQue(){40 int command=0;41 for(int i=0;i<100;i++){42 bool result =outMsgLULProc(command);43 if(result){44 //消息不空
45 cout<
47 }48 else{49 cout << "OutMsgQue执行,但是消息队列为空"<
52 }53 cout<
56 private:57 listMyQue;58 std::mutex my_mutex;//创建一个互斥量
59 };60
61 intmain(){62 A myobj;63 std::thread myout(&A::OutMsgQue,&myobj);64 std::thread myin(&A::InMsgQue,&myobj);//必须市引用,才能保证线程用的是同一个对象
65 myout.join();66 myin.join();67 cout<
2、std::lock_guard类模板
为了防止忘记unlock,引入lock_guard,类似于智能指针,他会帮你unlock。
1 bool outMsgLULProc(int &command){2 std::lock_guard<:mutex>guardd(my_mutex);3 if(!MyQue.empty()){4 int command =MyQue.front();5 MyQue.pop_front();6 return true;7 }8 return false;9 }
lock_guard构造函数执行了lock;析构函数执行了unlock。
只有return的时候才unlock,没有分开使用lock和unlock灵活。
可以用大括号提前结束guard:
1 voidInMsgQue(){2 for(int i=0;i<100;i++){3 cout<sbguard(my_mutex);6 MyQue.push_back(i);//假设i就是命令
7 }//大括号结束,lock_guard析构函数执行8 //其他处理代码、、、、、、、
9 }10
11 }
三、死锁
1、演示
举例:张三在北京等李四,不挪窝;李四在深圳等张三,不挪窝。
c++中:
死锁问题是有至少两个锁头也就是两个互斥量才会产生。
有两个锁:金锁,银锁
线程:A 和 B
线程A先锁金锁,金锁锁成功,然后去lock银锁。。。
线程B先锁银锁,银锁还没被锁的话,锁银锁成功了,然后去lock金锁。。。但是金锁被A锁了。。。
A拿不到银锁,金锁解不开,B去拿金锁,但是金锁被A锁了,所以银锁解不开。
这时就发生死锁了。AB两个线程就干等着直到永远~~
2、解决方案
只要保证两个互斥量的lock顺序一致,就不会发生死锁。
3、std::lock()函数模板
用来处理多个互斥量。
可以一次锁住两个或多个互斥量。
它不存在由于多个线程中因为锁的顺序问题导致的死锁。
如果一个锁没锁住,他就会马上解锁之前所有锁上的互斥量,然后在那儿等着,等所有的互斥量都能够锁住,它才能往下走。
要么都锁住,要么都没有锁住。
1 voidInMsgQue(){2 for(int i=0;i<100;i++){3 cout<
5 MyQue.push_back(i);//假设i就是命令
6 my_mutex1.unlock();7 my_mutex2.unlock();8 //其他处理代码、、、、、、、
9 }10
11 }
容易忘记unlock
4、std::lock_guard的std::adopt_lock参数
使用lock模板,还是容易忘记unlock,那么可是使用lock_guard的adopt_lock参数:
1 voidInMsgQue(){2 for(int i=0;i<100;i++){3 cout<
std::lock(my_mutex1,my_mutex2);4 std::lock_guard<:mutex> sbguard(my_mutex1,std::adopt_lock);5 std::lock_guard<:mutex> sbguard2(my_mutex2,std::adopt_lock);6 MyQue.push_back(i);//假设i就是命令7 //其他处理代码、、、、、、、
8 }9 }
这个参数就是起一个标记作用,就是如果互斥量已经被lock了,那就不会再调用lock_guard的构造函数了,就只剩析构函数了