生活中的死锁场景
例子1
比如在现实生活中
有且仅有两个相邻的车位 分别命名为A B
这两个车位上分别停着两辆车 A车位停着车辆a B车位停着车辆b
现在 车辆a 车辆b 想要互换位置
但由于有且仅有两个车位 无法实现位置互换 (如果存在第三个空车位则可以实现)
车辆a想到B车位 但是B车位已经被车辆b占据了
车辆b想到A车位 但是A车位已经被车辆a占据了
例子2
有 甲乙丙丁 4个小朋友
甲 欠 乙10元
乙 欠 丙10元
丙 欠 丁10元
丁 欠 甲10元
这四个小朋友手里都没有钱
必须等待他人还钱 才能还给自己欠的人
因此 他们四个人永远处于等待中 无法实现还钱
程序中的死锁场景
资源1 资源2 是互斥性质的资源
同一时刻只能有一个线程使用
线程A需要先占用资源1 再占用资源2
线程B需要先占用资源2 再占用资源1
当线程A尝试占用资源2的时候 由于其已经被B占用 线程A进入阻塞
当线程B尝试占用资源1的时候 由于其已经被A占用 线程B进入阻塞
Linux环境中代码如下
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>
//线程互斥量变量
//用互斥锁分别模拟对资源1和资源2的占用
pthread_mutex_t mutex1;
pthread_mutex_t mutex2;
//线程A的处理函数
void *funA(void *arg)
{
//线程A先占用资源1
//再在占用资源1的情况下申请资源2
pthread_mutex_lock(&mutex1);
printf("线程A占用资源1\n");
pthread_mutex_lock(&mutex2);
printf("线程A占用资源2\n");
printf("线程A执行临界区代码\n");
//解锁
pthread_mutex_unlock(&mutex1);
pthread_mutex_unlock(&mutex2);
return NULL;
}
//线程B的处理函数
void *funB(void *arg)
{
//线程B先占用资源2
//再在占用资源2的情况下申请资源1
pthread_mutex_lock(&mutex2);
printf("线程B占用资源2\n");
pthread_mutex_lock(&mutex1);
printf("线程B占用资源1\n");
printf("线程B执行临界区代码\n");
//解锁
pthread_mutex_unlock(&mutex2);
pthread_mutex_unlock(&mutex1);
return NULL;
}
int main(void)
{
//获取函数的返回状态
int ret = -1;
//需要模拟线程A和线程B
pthread_t tidA, tidB;
//初始化互斥量
pthread_mutex_init(&mutex1, NULL);
pthread_mutex_init(&mutex2, NULL);
//创建线程A和线程B
pthread_create(&tidA, NULL, funA, NULL);
pthread_create(&tidB, NULL, funB, NULL);
//回收线程资源
ret = pthread_join(tidA, NULL);
if(0 != ret)
{
printf("pthread_join tidA failed\n");
return 1;
}
ret = pthread_join(tidB, NULL);
if(0 !=ret)
{
printf("pthread_join tidB failed\n");
return 1;
}
//销毁互斥量
pthread_mutex_destroy(&mutex1);
pthread_mutex_destroy(&mutex2);
return 0;
}
运行效果
多次执行 a.out
均未出现 "执行临界区代码" 的语句打印
这就意味着发生了死锁
在没有外界干扰的情况下 两个线程将永远阻塞
如何解决死锁场景
1.若长时间阻塞 释放已占用的资源后 再申请新的资源
2.一开始就一次性获取所有需要的资源
3.额外规定请求资源的顺序