發生死鎖必須具有如下的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的原理就是這個