c++介绍锁四
对于多线程来说导致死锁的原因较多,最简单的一个原因是锁定一个互斥量后,没有释放这个互斥量,导致其它的线程获取这个互斥量时,会永远进入阻塞状态,往往发生在程序分支较多或者抛出异常时忘记主动释放,解决方法:lock_guard或者unique_lock这样的自动对象,对互斥量进行管理,这样离开作用域后,在析构函数中自动释放互斥量。
另一个原因,同时对多个互斥量进行锁定和解锁时,由于获得和释放互斥量顺序不同容易造成死锁。
例如
#include<array>
#include<thread>
#include<iostream>
#include<mutex>
using namespace std;
class DemoClass
{
public:
void func1()
{
{
lock_guard<mutex> lock1(mtx1);
lock_guard<mutex>lock2(count_mtx);
count++;
cout << "[func1]count=" << count << "\n";
}
this_thread::sleep_for(1ms);
}
void func2()
{
{
lock_guard<mutex>lock1(count_mtx);
lock_guard<mutex>lock2(mtx1);
count--;
cout << "[fuc2]Count=" << count << "\n";
}
this_thread::sleep_for(1ms);
}
void calc(int n)
{
for (int i = 0; i < n; i++)
{
if (n % 2)
{
func2();
}
else
{
func1();
}
}
}
private:
mutex mtx1;
mutex count_mtx;
int count = 0;
};
void main()
{
DemoClass demo;
const int n1 = 10000;
const int n2 = 10001;
thread th1(&DemoClass::calc,&demo,n1);
thread th2(&DemoClass::calc,&demo, n2);
th1.join();
th2.join();
}
运行结果
程序运行一段时间后发生死锁。
线程1获取mtx1等待获取count_mtx,而线程2获取count_mtx等待获取mtx这样程序永远进入阻塞状态。
改进方法1代码如下
#include<array>
#include<thread>
#include<iostream>
#include<mutex>
using namespace std;
class DemoClass
{
public:
void func1()
{
while (true){
unique_lock<timed_mutex> lock1(mtx1,defer_lock);
unique_lock<timed_mutex>lock2(count_mtx,defer_lock);
if (!lock1.try_lock_for(100ms))
continue;
if (!lock2.try_lock_for(100ms))
continue;
count++;
cout << "[func1]count=" << count << "\n";
}
this_thread::sleep_for(1ms);
}
void func2()
{
while(true){
unique_lock<timed_mutex>lock1(count_mtx,defer_lock);
unique_lock<timed_mutex>lock2(mtx1,defer_lock);
if (!lock1.try_lock_for(100ms))
continue;
if (!lock2.try_lock_for(100ms))
continue;
count--;
cout << "[fuc2]Count=" << count << "\n";
}
this_thread::sleep_for(1ms);
}
void calc(int n)
{
for (int i = 0; i < n; i++)
{
if (n % 2)
{
func2();
}
else
{
func1();
}
}
}
private:
timed_mutex mtx1;
timed_mutex count_mtx;
int count = 0;
};
void main()
{
DemoClass demo;
const int n1 = 10000;
const int n2 = 10001;
thread th1(&DemoClass::calc,&demo,n1);
thread th2(&DemoClass::calc,&demo, n2);
th1.join();
th2.join();
}
使用计时锁如果在100ms内为获取到想获取得锁,释放当前拥有得锁,进入下一次循环,这样程序进入到活锁得状态,两个线程不断得释放自己当前拥有得锁,而又得不到想获取得锁。
解决活锁得方法:保证顺序获得互斥量
#include<array>
#include<thread>
#include<iostream>
#include<mutex>
using namespace std;
class DemoClass
{
public:
void func1()
{
lock(mtx1,count_mtx);
lock_guard<mutex> lock1(mtx1,adopt_lock);
lock_guard<mutex> lock2(count_mtx, adopt_lock);
count++;
cout << "[func1]count=" << count << "\n";
this_thread::sleep_for(1ms);
}
void func2()
{
lock(mtx1, count_mtx);
lock_guard<mutex> lock1(mtx1, adopt_lock);
lock_guard<mutex> lock2(count_mtx, adopt_lock);
count--;
cout << "[fuc2]Count=" << count << "\n";
this_thread::sleep_for(1ms);
}
void calc(int n)
{
for (int i = 0; i < n; i++)
{
if (n % 2)
{
func2();
}
else
{
func1();
}
}
}
private:
mutex mtx1;
mutex count_mtx;
int count = 0;
};
void main()
{
DemoClass demo;
const int n1 = 10000;
const int n2 = 10001;
thread th1(&DemoClass::calc,&demo,n1);
thread th2(&DemoClass::calc,&demo, n2);
th1.join();
th2.join();
}
算法大致原理
线程1拥有互斥量A,线程2拥有互斥量b,当前程1尝试获取互斥量b时,获取失败,线程1释放互斥量a,此时线程2获取互斥量a成功,线程2再获取互斥量c,拥有所有资源后执行线程2.线程2执行完毕后释放互斥量a,b,c。线程1获取互斥量a,b,c.执行线程1。
任何一个下线程在等待资源得时候,不能够锁定任何资源。如果已经获得资源,那么需要释放资源,再进入阻塞状态。即防止死锁条件得破环请求保持条件。
此外还可以使用scope_lock类更加方便。