目录
一、概念
二、例子
三、死锁相关面试题目
一、概念
死锁主要发生在有多个依赖锁存在时,会在一个线程试图以另一个线程相反顺序锁住互斥量时发生
死锁使得一个或多个线程被挂起而无法继续执行,最糟糕的是,这种情况还不容易被发现。
在一个线程中对一个已经加锁的普通锁再次加锁,将导致死锁
这种情况可能出现在设计得不够仔细地递归函数中。
另外,如果两个线程按照不同的顺序来申请两个互斥锁,也容易发生死锁
二、例子
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <unistd.h>
#include <pthread.h>int a=0;
int b=0;
pthread_mutex_t mutex_a;
pthread_mutex_t mutex_b;
void *another(void *arg)
{pthread_mutex_lock(&mutex_b);printf("in child thread,got mutex b,waiting for mutex a\n");sleep(5);++b;pthread_mutex_lock(&mutex_a);b+=a++;pthread_mutex_unlock(&mutex_a);pthread_mutex_unlock(&mutex_b);pthread_exit(NULL);
}int main()
{pthread_t id;pthread_mutex_init(&mutex_a,NULL);pthread_mutex_init(&mutex_b,NULL);pthread_create(&id,NULL,another,NULL);pthread_mutex_lock(&mutex_a);printf("in parent thread,got mutex a,waiting for mutex b\n");sleep(5);++a;pthread_mutex_lock(&mutex_b);a+=b++;pthread_mutex_unlock(&mutex_b);pthread_mutex_unlock(&mutex_a);pthread_join(id,NULL);pthread_mutex_destroy(&mutex_a);pthread_mutex_destroy(&mutex_b);exit(0);
}
主线程试图先占有互斥锁mutex_a,然后操作被该锁保护的变量a,但操作完毕之后,主线程并没有立即释放互斥锁mutex_a,而是又申请互斥锁mutex b,并在两个互斥锁的保护下,操作变量a和b,最后才一起释放这两个互斥锁;
于此同时,子线程则按照相反的顺序来申请互斥锁mutex_a和mutex_b,并在两个锁的保护下操作变量a和b;
我们用sleep函数来模拟连续两次调用pthread_mutex_lock之间的时间差,以确保代码中的两个线程各自先占用一个互斥锁(主线程占有mutex_a,子线程占有mutex_b),然后等待另外一个互斥锁(主线程等待mutex_b,子线程等待mutex_a).这样,两个线程就僵持住了,谁也不能继续往下执行,从而形成死锁.
如果代码中不加入sleep函数,则这段代码或许总能成功地运行,从而为程序留下了一个潜在地bug.
如何避免死锁是使用互斥量应该格外注意的东西
- 总体来讲,有几个不成文的基本规则:
- 对共享资源操作前一定要获得锁;
- 完成操作以后一定要释放锁;
- 尽量短时间地占用锁;
- 如果有多个锁,如获得顺序是ABC连环扣.释放顺序也应该是ABC;
- 线程错误返回时应该释放它所获得的锁;
三、死锁相关面试题目
问题:如何避免死锁?
解答:
死锁的概念:多个进程或线程访问一组竟态资源的时候,出现的永久阻塞的问题。也可以这么说:指两个或两个以上的线程或进程在执行程序的过程中,因争夺资源或者程序推进顺序不当而相互等待的一个现象。
产生的原因主要有三个:系统资源不足,程序运行推进的顺序不当,资源分配不当。
或者说:死锁产生的必要条件是:
- 互斥条件
- 请求和保持条件
- 不剥夺条件
- 环路等待条件
避免死锁就是打破这四个条件中的某一个即可。如某个进程申请多个资源,只要有一个资源不满足暂时就不要分配任何资源。等所有资源能满足时一起分配。
处理死锁的基本方法?
- 预防死锁
- 避免死锁(银行家算法)
- 检测死锁(资源分配)
- 解除死锁:剥夺资源、撤销进程