死锁检测

摘要:
最终,公司的一个老员工,只能开发了一个死锁检测框架,在debug模式下运行时,只要发生死锁就会打印出调用堆栈。虽然说这个框架基本可以在上线前把所有的死锁都检测了出来,但是,规根到底这是设计不合理造成的,多线程利用好了会提升应用的效率,用不好的话,除了影响效率外,对上层开发简直是灾难。

曾经参与过的一款网络游戏,其服务器使用了异常复杂的多线程序解决方案。导致应用层程序员编写的代码很容易就出现死锁。

最终,公司的一个老员工,只能开发了一个死锁检测框架,在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();
}

免责声明:文章转载自《死锁检测》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Kubernetes核心原理(四)之KubeletJPA-映射MySql text类型下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

android中Handle类的用法

android中Handle类的用法     当我们在处理下载或是其他需要长时间执行的任务时,如果直接把处理函数放Activity的OnCreate或是OnStart中,会导致执行过程中整个Activity无响应,如果时间过长,程序还会挂掉。Handler就是把这些功能放到一个单独的线程里执行,与Activity互不影响。     当用户点击一个按钮时如果...

一道有意思的多线程面试题 C# 代码实现

如果你对多线程的控制不怎么了解,那么理解了这篇文章的内容也许对你有帮助。鼓励先自己动手实现一遍,做不出来在看代码。 题目一:两个线程交替打印0~100的奇偶数 这道题就是说有两个线程,一个名为偶数线程,一个名为奇数线程,偶数线程只打印偶数,奇数线程只打印奇数,两个线程按顺序交替打印。本文重点不是说的这道题,这道题是下面那道题的简单版本,用来做个过渡。 效果...

c# sleep 例子

using System; using System.Threading; public class arr { public static void Main() { //int[] arr; //arr = new int[5]; int luzi; for(luzi=1;luzi<10000;luzi++) { Console.WriteL...

Java回调实现异步 (转)

出处: Java回调实现异步   在正常的业务中使用同步线程,如果服务器每处理一个请求,就创建一个线程的话,会对服务器的资源造成浪费。因为这些线程可能会浪费时间在等待网络传输,等待数据库连接等其他事情上,真正处理业务逻辑的时间很短很短,但是其他线程在线程池满了之后又会阻塞,等待前面的线程处理完成。而且,会出现一个奇怪的现象,客户端的请求被阻塞,但是cpu的...

Python thread &amp;amp; process

线程 点击查看 <- 进程 点击查看 <- 线程与进程的区别 线程共享内部空间;进程内存空间独立 同一个进程的线程之间可以直接交流;两个进程之间想通信必须通过一个中间代理 创建新线程很简单,占用资源较少;创建新进程是对父进程的克隆,会占用很多资源 一个线程可以控制和操作同一进程里的其他线程;但是进程只能操作子进程 修改父线程可能会影响到进程下...

Android Handler消息传递

一、背景 出于性能优化考虑,Android的UI操作并不是线程安全的,这意味着如果有多个线程并发操作UI组件,可能导致线程安全问题。为了解决这个问题,Android制定了一条简单的原则:只允许UI线程(亦即主线程)修改Activity中的UI组件。 当一个程序第一次启动时,Android会同时启动一条主线程,主线程主要负责处理与UI相关的事件,如用户的按键...