一、背景
在工作项目使用多进程、多线程过程中,因争夺资源而造成一种资源竞态,所以需加锁处理。如下图所示,线程A想获取线程B的锁,线程B想获取线程C的锁,线程 C 想获取线程D的锁, 线程D想获取线程A的锁,从而构建了一个资源获取环,当进程或者线程申请的锁处于相互交叉锁住的情况,就会出现死锁,它们将无法继续运行。
死锁的存在是因为有资源获取环的存在,所以只要能检测出资源获取环,就等同于检测出死锁的存在。
二、原理
在不改变项目源代码的情况下,采用图算法来检测环的存在,使用有向图来存储;如线程A获取线程B已占用的锁(表示线程B获取锁成功),则为线程A指向线程B;启动一个线程定时对图进行检测是否有环的存在。
(1)数据结构
//数据/点
struct node{
uint64 thread_id;//线程ID
uint64 lock_id;//锁ID
int degress;
};
//数据和数据结构分开
struct vertex{
struct node *d;
struct vertex *next;
};
struct graph{
struct vertex list[THREAD_MAX];//存储图的所有节点
int num;//已经使用了多少个
struct node locklist[THREAD_MAX];
int lockidx;
pthread_mutex_t mutex;//预留:线程安全考虑,在对图修改时加锁
};
(2)图的操作
a.创建图节点
//创建图节点
struct vertex *create_vertex(struct node *d){
struct vertex *tex = (struct vertex*)calloc(1,sizeof(struct vertex));
if(tex == NULL) return NULL;
tex->d = d;
tex->next = NULL;
return tex;
}
b.查找节点
//查找节点,是否存在
int search_vertex(struct node *d){
int i;
for (i = 0; i < tg->num; i++)
{
if (tg->list[i].d->thread_id == d->thread_id)
{
return i;
}
}
c.添加节点
//添加节点,只是把添加的节点放到list中,还没有确定各节点间的指向,必须通过add_edge添加边来确定
void add_vertex(struct node *d){
if (search_vertex(d) == -1)
{
tg->list[tg->num].d = d;//添加到list中
tg->list[tg->num].next = NULL;
tg->num++;//节点数累加
}
}
d.添加边,指定方向
//添加边,指定方向,谁指向谁
void add_edge(struct no