線程死鎖一引發的系列思考

發生死鎖必須具有如下的4個條件函數

 

1)互斥條件:指進程對所分配到的資源進行排它性使用,即在一段時間內某資源只由一個進程佔用。若是此時還有其它進程請求資源,則請求者只能等待,直至佔有資源的進程用畢釋放。this

:資源是互斥的操作系統

2)請求和保持條件:指進程已經保持至少一個資源,但又提出了新的資源請求,而該資源已被其它進程佔有,此時請求進程阻塞,但又對本身已得到的其它資源保持不放。線程

:若是隻請求一個資源 那麼也不會發現設計

3)不剝奪條件:指進程已得到的資源,在未使用完以前,不能被剝奪,只能在使用完時由本身釋放。code

:全部權遞歸

4)環路等待條件:指在發生死鎖時,必然存在一個進程——資源的環形鏈,即進程集合{P0,P1,P2,···,Pn}中的P0正在等待一個P1佔用的資源;P1正在等待P2佔用的資源,……,Pn正在等待已被P0佔用的資源。進程

:好比操做系統課老師舉得例子 幾我的吃飯,拿筷子問題 ,最後都會沒拿到2只筷子而都吃不了飯資源

 

死鎖例子get

 static  std::recursive_mutex _mutex1;
static  std::recursive_mutex _mutex2;
int hp;

 int main(int argc, char *argv[])
{
  thread t ( []()
  {  
    _mutex1.lock();
    Sleep(12);
    _mutex2.lock();
   cout << "t:"<< hp << endl;
 
    _mutex1.unlock();
   _mutex2.unlock();
  });
  t.detach();
 
  _mutex2.lock();
  Sleep(12);
  _mutex1.lock();
  cout << "main:" << hp << endl;
 

  _mutex1.unlock();
  _mutex2.unlock();
 system("pause");
 return 0;
}

mutex1 mutex2 爲2個資源,形成了環路等待條件 致使線程死鎖

 

遞歸問題:遞歸加鎖了 如

void doWith()
{
 _mutex1.lock();
 cout << "doWith:" << hp << endl;
  _mutex1.unlock();

} 
  int main(int argc, char *argv[])
 {
 
  thread t ( [=]()
  {  
    _mutex1.lock();
 
   cout << "t:"<< hp << endl;
   doWith();
     _mutex1.unlock();
 
  });
  t.detach();
  system("pause");
 return 0;
}

mutex被遞歸加鎖了   而 std::mutex 非遞歸鎖,子程序和 匿名函數  位於同一個線程空間     就又會形成 死鎖,由於子程序在等待 匿名函數的解鎖,而進入死鎖

這時候可用 std::recursive_mutex  遞歸鎖 替代std::mutex  或者本身實現一個遞歸鎖

原理大體是 若是 和上次成功lock的操做的線程 不在同一線程 就真正的 lock 不然僅僅是引用計數加1

 如下是個人一個實現

class Re_mutex
{
public:
 void lock()
 {
  cout << "lock id "<<this_thread::get_id() << endl;
  if (this_thread::get_id() == _last_lock_id &&_islock == true)
  {
   ++count;
  }
  else
  {
   _last_lock_id = this_thread::get_id();
   _islock = true;
   _mutex.lock();
  }
 }

 void unlock()
 {
  --count;
  if (count == 0 && _islock)
  {
   _mutex.unlock();
  }
 }
private:
 bool _islock = false;
 mutex _mutex;
 int count = 0;
 thread::id _last_lock_id;
};
static  Re_mutex _mutex1;
//static  std::recursive_mutex _mutex2;
int hp;
void doWith()
{
 _mutex1.lock();
 cout << "doWith:" << hp << endl;
 _mutex1.unlock();

}

 int main(int argc, char *argv[])
 {
 
  thread t ( [=]()
  {  
    _mutex1.lock();
 
   cout << "t:"<< hp << endl;
   doWith();
    _mutex1.unlock();
 
  });
  t.detach();
 
 
 system("pause");
 return 0;
}

解決死鎖的代價比較大,因此通常解決辦法是 經過良好的設計 避免 死鎖

 

標準庫提供了一套 避免死鎖的方案

std::lock  該鎖 把須要訪問的資源 要麼都加鎖 要麼都不加鎖 來避免死鎖

template<class _Lock0,
	class _Lock1,
	class... _LockN> inline
	void lock(_Lock0& _Lk0, _Lock1& _Lk1, _LockN&... _LkN)
	{	// lock N mutexes
	int _Res = 0;
	while (_Res != -1)
		_Res = _Try_lock(_Lk0, _Lk1, _LkN...);
	}

 

lock_guard能夠經過調用重載構造函數 來避免加鎖 ,來RAII資源

std::lock_guard<std::mutex>  l(m,std::adopt_lock);

 

避免死鎖的一些方法

避免嵌套鎖
一個線程已經得到了一個鎖的同時 別去獲取第二個 若是須要 那就用std::lock 來獲取

避免在持有鎖的狀況下調用用戶代碼,由於你不知道永不要作什麼

使用固定順序獲取鎖,如獲取了A才能獲取B,這樣就不會致使循環等待的狀況, 固然std::lock的原理就是這個
相關文章
相關標籤/搜索