互斥量概念、用法、死锁演示及解决详解
我的实践
#include <iostream>
#include <thread>
#include <list>
#include <mutex>
#define N 1000
using namespace std;
class T {
private:
list<int> lst;
mutex my_mutex;
mutex my_mutex1;
mutex my_mutex2;
public:
/* void InMsg() {
for (int i = 0; i < N; i++) {
my_mutex.lock();
cout << "插入一个数" << i << endl;
lst.push_back(i);
my_mutex.unlock();
}
}
//写法1
void OutMsg() {
for (int i = 0; i < N; i++) {
my_mutex.lock();
if (lst.empty()) {
cout << "空的" << endl;
}
else {
cout << "取出一个数"<< lst.front() << endl;
lst.pop_front();
}
my_mutex.unlock();
}
cout << "结束了" << endl;
}
//写法2
/*bool outMsgLULProc(int &command)
{
my_mutex.lock();
if (!lst.empty())
{
command = lst.front();
lst.pop_front();
my_mutex.unlock();
return true;
}
my_mutex.unlock();
return false;
}
void OutMsg()
{
int command = 0;
for (int i = 0; i < N; ++i)
{
if (outMsgLULProc(command))
{
cout << "取出一个元素" << command << endl;
}
else
{
cout << "空数组为空" << endl;
}
}
}*/
//写法3 lock_guard()
/* void InMsg() {
for (int i = 0; i < N; i++) {
my_mutex.lock();
cout << "插入一个数" << i << endl;
lst.push_back(i);
my_mutex.unlock();
}
}
void OutMsg() {
for (int i = 0; i < N; i++) {
lock_guard<mutex> my_guard(my_mutex);//
if (lst.empty()) {
cout << "空的" << endl;
}
else {
cout << "取出一个数" << lst.front() << endl;
lst.pop_front();
}
}
cout << "结束了" << endl;
} */
//死锁---
/* void InMsg() {
for (int i = 0; i < N; i++) {
my_mutex1.lock();//实际过程中两个锁不会紧挨着。
my_mutex2.lock();
cout << "插入一个数" << i << endl;
lst.push_back(i);
my_mutex2.unlock();
my_mutex1.unlock();
}
}
void OutMsg() {
for (int i = 0; i < N; i++) {
my_mutex2.lock();
my_mutex1.lock();//先锁mutex2再锁1,与InMsg相反。会发生死锁。顺序一致就不会。
if (lst.empty()) {
cout << "空的" << endl;
}
else {
cout << "取出一个数"<< lst.front() << endl;
lst.pop_front();
}
my_mutex1.unlock();
my_mutex2.unlock();
}
cout << "结束了" << endl;
} */
// 死锁-- 用lock()锁两个,然后使用lock_guard的adopt_lock参数,就不用unlock了
void InMsg() {
for (int i = 0; i < N; i++) {
lock(my_mutex1, my_mutex2);
lock_guard<mutex> guard1(my_mutex1, adopt_lock);
lock_guard<mutex> guard2(my_mutex2, adopt_lock);
cout << "插入一个数" << i << endl;
lst.push_back(i);
}
}
void OutMsg() {
for (int i = 0; i < N; i++) {
lock(my_mutex1, my_mutex2);
lock_guard<mutex> guard1(my_mutex1, adopt_lock);
lock_guard<mutex> guard2(my_mutex2, adopt_lock);
if (lst.empty()) {
cout << "空的" << endl;
}
else {
cout << "取出一个数" << lst.front() << endl;
lst.pop_front();
}
}
cout << "结束了" << endl;
}
};
int main() {
//1.创建和等待多个线程
T myobj;
thread myThread1(&T::InMsg, std::ref(myobj));//分别是函数地址,对象名的引用,和函数参数(如果有的话)
thread myThread2(&T::OutMsg, std::ref(myobj));
myThread1.join();
myThread2.join();
cout<<"程序结束"<<endl;
getchar();
}
// 操作时把共享和数据锁住,其他想操作共享数据的线程必须等待
//2.互斥量
// 1).基本概念
// 互斥锁(mutex) ----------保护共享数据
// 互斥量就是个类对象。理解成一把锁,多个线程尝试用lock成员函数尝试加锁。
// 如果没锁成功就一直卡在lock这一阶段,一直尝试
// 2).
// lock,unlock ,先lock在unlock,并且必然是成对使用的
//3).为防止用户忘记unlock,引入lock_guard()类模板,但是用了之后就不能再用lock和unlock了
// lock_guard构造函数执行了lock(), 析构函数执行了unlock()
// 3.死锁
// 1)至少有两个互斥量。Jinlock,Yinlock。两个线程A,B,
// 2)A先Jinlock在Yinlock,B先Yinlock再Jinlock
//3) 线程A去jINlock, 再Yinlock.-----但是锁Yinlock的时候上下文切换
//线程B去Yinlock,因为Yinlock还没锁成功,所有线程B可以锁成功。然后再去Jinlock
// 4)然后死锁发生了A拿不到Yin锁,B拿不到Jin锁。
// 4.死锁的一般解决方案
// 1)都按同样的顺序锁
// 2)用lock_guard
//5. lock()的使用+:
//能力:一次锁住两个或以上互斥量
//他不存在这种因为在多个线程中因为锁的顺序问题导致思索的风险问题。
//但是实际中很少同时锁住多个互斥量的情况
//6:lock_guard()的adopt参数
// 表示之前已经Lock了,无需再lock了。