什么是死锁?
线程A 需要拿到两个mutex才能执行完毕,一个mutex1,一个mutex2,注意是有顺序的
线程B 需要拿到两个mutex才能执行完毕,一个mutex2,一个mutex1,注意是有顺序的
当线程A 拿到mutex1后,马上去拿mutex2,这时候由于线程调度。
系统调用线程B,这时候线程B 是可以拿到mutex2,但是拿不到mutex1,因为mutex1是被线程A拿着的,
于是就变成了A 拿着mutex1,然后等mutex2才能往下走。
B 拿着mutex2,然后等mutex1才能往下走。
都卡着,于是死锁发生了
死锁发生的条件
1.需要至少两个mutex。
2.需要至少两个线程。
3.两个线程需要同样的多个mutex
---这句话的意思是: A 线程 需要 mutex1,mutex2
B线程也需要mutex1,和mutex2
4.线程A 和线程B 对于mutex1,和 mutex2 的需求顺序不同。
-----这句话的意思是:A线程需要 mutex1保护一段数据,然后需要mutex2保护一段数据。
而B线程是先需要mutex2保护一段数据,然后再需要mutex1保护一段数据
死锁演示
//死锁演示
class Teacher167 {
public:
//共享数据 存在list中
list<int> msgListQueue;
mutex mymutex1;
mutex mymutex2;
int readcount = 0;//记录已经读取了多少个。
public:
//读取 共享数据的线程方法
void readfunc() {
while (true) {
//只要不为空,就可以读取数据
if (readcount >= 20000) {
break;
}
{
//先使用mymutex1 处理一段数据
mymutex1.lock();//mylock_guard在这里创建出来,在mylock_guard的构造函数中,就已经对mymutex进行了lock()操作,等到mylock_guard的生命周期结束后会析构,这时候会调用mymutex的unlock()函数
mymutex2.lock();
if (!msgListQueue.empty()) {
int readvalue = msgListQueue.front();//每次都读取第一个
cout << "读取到的值为" << readvalue << " readcount = " << readcount << endl;
msgListQueue.pop_front();//删除第一个元素
readcount++;
mymutex1.unlock();
mymutex2.unlock();
}
else {
cout << "没有读取到值" << endl;
mymutex1.unlock();
mymutex2.unlock();
}
}
}
}
//写入 共享数据的线程方法
void writefunc() {
for (size_t i = 0; i < 10000; i++)
{
mymutex2.lock();
mymutex1.lock();
msgListQueue.push_back(i);//每次都写到末尾
cout << "写入元素的值为" << i << endl;
mymutex1.unlock();
mymutex2.unlock();
}
}
public:
Teacher167() {
cout << "Teacher167 构造方法 this = " << this << endl;
}
Teacher167(const Teacher167 & obj) {
cout << "Teacher167 copy 构造方法 this = " << this << " obj = " << &obj << endl;
}
~Teacher167() {
cout << "Teacher167 析构函数 this = " << this << endl;
}
};
void main() {
cout << "=========================" << endl;
Teacher167 tea1;
thread readthread1(&Teacher167::readfunc, &tea1);
thread writethread1(&Teacher167::writefunc, &tea1);
readthread1.join();
writethread1.join();
}
死锁演示2
访问不同的数据也是一样的
//死锁演示2
class Teacher168 {
public:
//共享数据 存在list中
list<int> msgListQueue;
list<int> msgListQueue2;
mutex mymutex1;
mutex mymutex2;
int readcount = 0;//记录已经读取了多少个。
public:
//读取 共享数据的线程方法
void readfunc() {
while (true) {
//只要不为空,就可以读取数据
if (readcount >= 20000) {
break;
}
//先使用mymutex1 处理一段数据
mymutex1.lock();//mylock_guard在这里创建出来,在mylock_guard的构造函数中,就已经对mymutex进行了lock()操作,等到mylock_guard的生命周期结束后会析构,这时候会调用mymutex的unlock()函数
if (!msgListQueue.empty()) {
int readvalue = msgListQueue.front();//每次都读取第一个
cout << "读取到的值为" << readvalue << " readcount = " << readcount << endl;
msgListQueue.pop_front();//删除第一个元素
readcount++;
}
else {
cout << "没有读取到值" << endl;
}
mymutex2.lock();
if (!msgListQueue2.empty()) {
int readvalue = msgListQueue2.front();//每次都读取第一个
cout << "222读取到的值为" << readvalue << " readcount = " << readcount << endl;
msgListQueue2.pop_front();//删除第一个元素
readcount++;
}
else {
cout << "222没有读取到值" << endl;
}
mymutex1.unlock();
mymutex2.unlock();
}
}
//写入 共享数据的线程方法
void writefunc() {
for (size_t i = 0; i < 10000; i++)
{
mymutex2.lock();
mymutex1.lock();
msgListQueue.push_back(i);//每次都写到末尾
cout << "写入元素的值为" << i << endl;
mymutex1.unlock();
mymutex2.unlock();
}
}
public:
Teacher168() {
cout << "Teacher168 构造方法 this = " << this << endl;
}
Teacher168(const Teacher168 & obj) {
cout << "Teacher168 copy 构造方法 this = " << this << " obj = " << &obj << endl;
}
~Teacher168() {
cout << "Teacher168 析构函数 this = " << this << endl;
}
};
void main() {
cout << "=========================" << endl;
Teacher168 tea1;
thread readthread1(&Teacher168::readfunc, &tea1);
thread writethread1(&Teacher168::writefunc, &tea1);
thread readthread2(&Teacher168::readfunc, &tea1);
thread writethread2(&Teacher168::writefunc, &tea1);
readthread1.join();
writethread1.join();
readthread1.join();
writethread1.join();
}
fix 死锁问题
方案一:只要保证着两个互斥量的顺序一致,就不会死锁。使用lock_guard也是一样的。
class Teacher168 {
public:
//共享数据 存在list中
list<int> msgListQueue;
list<int> msgListQueue2;
mutex mymutex1;
mutex mymutex2;
int readcount = 0;//记录已经读取了多少个。
public:
//读取 共享数据的线程方法
void readfunc() {
while (true) {
//只要不为空,就可以读取数据
if (readcount >= 20000) {
break;
}
//先使用mymutex1 处理一段数据
mymutex1.lock();//mylock_guard在这里创建出来,在mylock_guard的构造函数中,就已经对mymutex进行了lock()操作,等到mylock_guard的生命周期结束后会析构,这时候会调用mymutex的unlock()函数
if (!msgListQueue.empty()) {
int readvalue = msgListQueue.front();//每次都读取第一个
cout << "读取到的值为" << readvalue << " readcount = " << readcount << endl;
msgListQueue.pop_front();//删除第一个元素
readcount++;
}
else {
cout << "没有读取到值" << endl;
}
mymutex2.lock();
if (!msgListQueue2.empty()) {
int readvalue = msgListQueue2.front();//每次都读取第一个
cout << "222读取到的值为" << readvalue << " readcount = " << readcount << endl;
msgListQueue2.pop_front();//删除第一个元素
readcount++;
}
else {
cout << "222没有读取到值" << endl;
}
mymutex1.unlock();
mymutex2.unlock();
}
}
//写入 共享数据的线程方法
void writefunc() {
for (size_t i = 0; i < 10000; i++)
{
//fix方案,让 mymutex1.lock(),和 mymutex2.lock() 的顺序和前面的一样。
mymutex1.lock();
mymutex2.lock();
msgListQueue.push_back(i);//每次都写到末尾
cout << "写入元素的值为" << i << endl;
mymutex1.unlock();
mymutex2.unlock();
}
}
public:
Teacher168() {
cout << "Teacher168 构造方法 this = " << this << endl;
}
Teacher168(const Teacher168 & obj) {
cout << "Teacher168 copy 构造方法 this = " << this << " obj = " << &obj << endl;
}
~Teacher168() {
cout << "Teacher168 析构函数 this = " << this << endl;
}
};
void main() {
cout << "=========================" << endl;
Teacher168 tea1;
thread readthread1(&Teacher168::readfunc, &tea1);
thread writethread1(&Teacher168::writefunc, &tea1);
thread readthread2(&Teacher168::readfunc, &tea1);
thread writethread2(&Teacher168::writefunc, &tea1);
readthread1.join();
writethread1.join();
readthread1.join();
writethread1.join();
}
方案二:std::lock(mutex1,mutex2)函数
使用lock(mutex1,mutex2); 参数都是mutex,代表的就是顺序,因此保证每个地方的调用都是一样的就可以了
lock(mymutex1, mymutex2);
class Teacher169 {
public:
//共享数据 存在list中
list<int> msgListQueue;
list<int> msgListQueue2;
mutex mymutex1;
mutex mymutex2;
int readcount = 0;//记录已经读取了多少个。
public:
//读取 共享数据的线程方法
void readfunc() {
while (true) {
//只要不为空,就可以读取数据
if (readcount >= 20000) {
break;
}
//fix方案2.使用lock(mutex1,mutex2); 参数都是mutex,代表的就是顺序,因此保证每个地方的调用都是一样的就可以了
lock(mymutex1, mymutex2);
if (!msgListQueue.empty()) {
int readvalue = msgListQueue.front();//每次都读取第一个
cout << "读取到的值为" << readvalue << " readcount = " << readcount << endl;
msgListQueue.pop_front();//删除第一个元素
readcount++;
}
else {
cout << "没有读取到值" << endl;
}
if (!msgListQueue2.empty()) {
int readvalue = msgListQueue2.front();//每次都读取第一个
cout << "222读取到的值为" << readvalue << " readcount = " << readcount << endl;
msgListQueue2.pop_front();//删除第一个元素
readcount++;
}
else {
cout << "222没有读取到值" << endl;
}
mymutex1.unlock();
mymutex2.unlock();
}
}
//写入 共享数据的线程方法
void writefunc() {
for (size_t i = 0; i < 10000; i++)
{
//fix方案2,使用lock(mutex1,mutex2); 参数都是mutex,代表的就是顺序,因此保证每个地方的调用都是一样的就可以了
lock(mymutex1, mymutex2);
msgListQueue.push_back(i);//每次都写到末尾
cout << "写入元素的值为" << i << endl;
mymutex1.unlock();
mymutex2.unlock();
}
}
public:
Teacher169() {
cout << "Teacher169 构造方法 this = " << this << endl;
}
Teacher169(const Teacher169 & obj) {
cout << "Teacher169 copy 构造方法 this = " << this << " obj = " << &obj << endl;
}
~Teacher169() {
cout << "Teacher169 析构函数 this = " << this << endl;
}
};
void main() {
cout << "=========================" << endl;
Teacher169 tea1;
thread readthread1(&Teacher169::readfunc, &tea1);
thread writethread1(&Teacher169::writefunc, &tea1);
thread readthread2(&Teacher169::readfunc, &tea1);
thread writethread2(&Teacher169::writefunc, &tea1);
readthread1.join();
writethread1.join();
readthread1.join();
writethread1.join();
}
方案二进阶版:使用 lock_guard<mutex> 替代 unlock
//fix方案进阶版.使用lock(mutex1,mutex2); 参数都是mutex,代表的就是顺序,因此保证每个地方的调用都是一样的就可以了
// 这里使用 lock_guard<mutex> sbguard1(mymutex1,adopt_lock);的含义是:在lock_guard构造函数中不调用 mutex1.lock(),只是在析构函数中调用 mutex1.unlock();
lock(mymutex1, mymutex2);
lock_guard<mutex> sbguard1(mymutex1,adopt_lock);
lock_guard<mutex> sbguard2(mymutex2, adopt_lock);
class Teacher170 {
public:
//共享数据 存在list中
list<int> msgListQueue;
list<int> msgListQueue2;
mutex mymutex1;
mutex mymutex2;
int readcount = 0;//记录已经读取了多少个。
public:
//读取 共享数据的线程方法
void readfunc() {
while (true) {
//只要不为空,就可以读取数据
if (readcount >= 20000) {
break;
}
//fix方案3.使用lock(mutex1,mutex2); 参数都是mutex,代表的就是顺序,因此保证每个地方的调用都是一样的就可以了
// 这里使用 lock_guard<mutex> sbguard1(mymutex1,adopt_lock);的含义是:在lock_guard构造函数中不调用 mutex1.lock(),只是在析构函数中调用 mutex1.unlock();
lock(mymutex1, mymutex2);
lock_guard<mutex> sbguard1(mymutex1,adopt_lock);
lock_guard<mutex> sbguard2(mymutex2, adopt_lock);
if (!msgListQueue.empty()) {
int readvalue = msgListQueue.front();//每次都读取第一个
cout << "读取到的值为" << readvalue << " readcount = " << readcount << endl;
msgListQueue.pop_front();//删除第一个元素
readcount++;
}
else {
cout << "没有读取到值" << endl;
}
if (!msgListQueue2.empty()) {
int readvalue = msgListQueue2.front();//每次都读取第一个
cout << "222读取到的值为" << readvalue << " readcount = " << readcount << endl;
msgListQueue2.pop_front();//删除第一个元素
readcount++;
}
else {
cout << "222没有读取到值" << endl;
}
}
}
//写入 共享数据的线程方法
void writefunc() {
for (size_t i = 0; i < 10000; i++)
{
//fix方案2,使用lock(mutex1,mutex2); 参数都是mutex,代表的就是顺序,因此保证每个地方的调用都是一样的就可以了
lock(mymutex1, mymutex2);
lock_guard<mutex> sbguard1(mymutex1, adopt_lock);
lock_guard<mutex> sbguard2(mymutex2, adopt_lock);
msgListQueue.push_back(i);//每次都写到末尾
cout << "写入元素的值为" << i << endl;
}
}
public:
Teacher170() {
cout << "Teacher170 构造方法 this = " << this << endl;
}
Teacher170(const Teacher170 & obj) {
cout << "Teacher170 copy 构造方法 this = " << this << " obj = " << &obj << endl;
}
~Teacher170() {
cout << "Teacher170 析构函数 this = " << this << endl;
}
};
void main() {
cout << "=========================" << endl;
Teacher170 tea1;
thread readthread1(&Teacher170::readfunc, &tea1);
thread writethread1(&Teacher170::writefunc, &tea1);
thread readthread2(&Teacher170::readfunc, &tea1);
thread writethread2(&Teacher170::writefunc, &tea1);
readthread1.join();
writethread1.join();
readthread2.join();
writethread2.join();
}