死鎖檢測


曾經參與過的一款網絡游戲,其服務器使用了異常復雜的多線程序解決方案。導致應用層程序員編寫的代碼很容易就出現死鎖。

最終,公司的一個老員工,只能開發了一個死鎖檢測框架,在debug模式下運行時,只要發生死鎖就會打印出調用堆棧。

雖然說這個框架基本可以在上線前把所有的死鎖都檢測了出來,但是,規根到底這是設計不合理造成的,多線程利用好了會提升

應用的效率,用不好的話,除了影響效率外,對上層開發簡直是災難。

 

下面說說那個檢測方法,其實方法挺簡單的。

有兩個容器,一個用於保存線程正在請求的鎖,一個用於保存線程已經持有的鎖。每次加鎖之前都會做如下檢測:

 

1)檢測當前正在請求的鎖是否已經被其它線程持有,如果有,則把那些線程找出來

2)遍歷第一步中返回的線程,檢查自己持有的鎖是否正被其中任何一個線程請求

 如果第二步返回真,表示出現了死鎖

 

下面是簡單的實現:

appMutex.h

#ifndef _APPMUTEX_H
#define _APPMUTEX_H
#include <iostream>
#include "lock.h"
#include <map>
#include <list>
class appMutex;
static Lock gMtx;

//記錄了線程當前正在請求的鎖
class mtxReqMgr
{
public:
static bool check(pthread_t pid);
static void reqMutex(appMutex *mtx);
static void clearReq(appMutex *mtx);
private:
static std::map<pthread_t,appMutex*> reqMap;//每個線程只可能請求一個鎖
};
//記錄了線程已經持有的鎖
class mtxHoldMgr
{
public:
static bool check(pthread_t pid,appMutex *mtx);
static bool check(appMutex *mtx,std::list<pthread_t> &ret);
static void hold(appMutex *mtx);
static void release(appMutex *mtx);
//釋放掉所有已經持有的鎖
static void releaseAll();

private:
static std::map<pthread_t,std::list<appMutex*> > holdMap;//每個線程可能擁有好幾個鎖
};

class appMutex : private Lock
{
friend class mtxHoldMgr;
public:
appMutex(const char *name):name(name)
{}

void lock()
{
/*這里執行死鎖檢測,檢測規則
1)檢測當前正在請求的鎖是否已經被其它線程持有,如果有,則把那些線程找出來
2)遍歷第一步中返回的線程,檢查自己持有的鎖是否正被其中任何一個線程請求
如果第二步返回真,表示出現了死鎖
*/
std::list<pthread_t> mtxHolds;
if(mtxHoldMgr::check(this,mtxHolds))
{
std::list<pthread_t>::iterator it = mtxHolds.begin();
std::list<pthread_t>::iterator end = mtxHolds.end();
for( ; it != end; ++it)
{
if(mtxReqMgr::check(*it))
{
mtxHoldMgr::releaseAll();
printf("dead lock in require %s,thread:%u/n",name.c_str(),pthread_self());
exit(0);
}
}
}

mtxReqMgr::reqMutex(this);
Lock::lock();
mtxReqMgr::clearReq(this);
mtxHoldMgr::hold(this);
}

void unlock()
{
Lock::unlock();
mtxHoldMgr::release(this);
}
private:
void release()//只有在出現死鎖時才會調用
{
Lock::unlock();
}

private:
std::string name;
};
#endif

appMutex.cpp

#include "appMutex.h"

std::map<pthread_t,appMutex*> mtxReqMgr::reqMap;
std::map<pthread_t,std::list<appMutex*> > mtxHoldMgr::holdMap;
//釋放掉所有已經持有的鎖
void mtxHoldMgr::releaseAll()
{
Scope_lock _guard(gMtx);
pthread_t pid = pthread_self();
std::map<pthread_t,std::list<appMutex*> >::iterator it = holdMap.find(pid);
if(it != holdMap.end())
{
while(!it->second.empty())
{
appMutex *_appmtx = it->second.back();
it->second.pop_back();
_appmtx->release();
}
}
}
bool mtxReqMgr::check(pthread_t pid)
{
Scope_lock _guard(gMtx);
pthread_t selfpid = pthread_self();
std::map<pthread_t,appMutex*>::iterator it = reqMap.find(pid);
if(it != reqMap.end() && it->second != NULL)
{
return mtxHoldMgr::check(selfpid,it->second);
}
return false;
}
void mtxReqMgr::reqMutex(appMutex *mtx)
{
Scope_lock _guard(gMtx);
pthread_t pid = pthread_self();
std::map<pthread_t,appMutex*>::iterator it = reqMap.find(pid);
if(it == reqMap.end())
{
reqMap.insert(std::make_pair(pid,mtx));
}
else
{
it->second = mtx;
}
}
void mtxReqMgr::clearReq(appMutex *mtx)
{
Scope_lock _guard(gMtx);
pthread_t pid = pthread_self();
std::map<pthread_t,appMutex*>::iterator it = reqMap.find(pid);
if(it != reqMap.end())
it->second = NULL;
else
{
printf("it must be error %s %d /n",__FILE__,__LINE__);
}
}
bool mtxHoldMgr::check(pthread_t pid,appMutex *mtx)
{
Scope_lock _guard(gMtx);
std::map<pthread_t,std::list<appMutex*> >::iterator it = holdMap.find(pid);
if(it != holdMap.end())
{
std::list<appMutex*>::iterator lit = it->second.begin();
std::list<appMutex*>::iterator lend = it->second.end();
for( ; lit != lend; ++lit)
{
if(mtx == *lit)
{
return true;
}
}
}
return false;
}
bool mtxHoldMgr::check(appMutex *mtx,std::list<pthread_t> &ret)
{
Scope_lock _guard(gMtx);
pthread_t pid = pthread_self();
std::map<pthread_t,std::list<appMutex*> >::iterator it = holdMap.begin();
std::map<pthread_t,std::list<appMutex*> >::iterator end = holdMap.end();
for( ; it != end; ++it)
{
if(it->first == pid)
continue;
std::list<appMutex*>::iterator lit = it->second.begin();
std::list<appMutex*>::iterator lend = it->second.end();
for( ; lit != lend; ++lit)
{
if(mtx == *lit)
{
ret.push_back(it->first);
break;
}
}

}
return !ret.empty();
}
void mtxHoldMgr::hold(appMutex *mtx)
{
Scope_lock _guard(gMtx);
pthread_t pid = pthread_self();
std::map<pthread_t,std::list<appMutex*> >::iterator it = holdMap.find(pid);
if(it == holdMap.end())
{
std::list<appMutex*> tmp;
tmp.push_back(mtx);
holdMap.insert(std::make_pair(pid,tmp));
}
else
{
it->second.push_back(mtx);
}
}
void mtxHoldMgr::release(appMutex *mtx)
{
Scope_lock _guard(gMtx);
pthread_t pid = pthread_self();
std::map<pthread_t,std::list<appMutex*> >::iterator it = holdMap.find(pid);
if(it != holdMap.end())
{
if(mtx != it->second.back())
{
//釋放鎖的順序跟加鎖的順序不一致
printf("it must be error %s %d /n",__FILE__,__LINE__);
}
else
it->second.pop_back();
}
else
printf("it must be error %s %d /n",__FILE__,__LINE__);
}

在兩個線程中分別如下調用,就會看到死鎖警告了

 

appMutex a("a");

appMutex b("b");





threada:



while(1)

{



a.lock();

Thread::sleep(1);

b.lock();



b.unlock();

a.unlock();

}





threadb:





while(1)

{

b.lock();

a.lock();



a.unlock();

b.unlock();



}

 

 

 





免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM