线程:死锁

10 篇文章 2 订阅

一.死锁原理

死锁可以定义为一组相互竞争系统资源或进行通信的进程间的“永久”阻塞。当一组进程中每个线程都在等待某个事件(典型的就是等待所请求的资源被释放),而只有这组进程的其他被阻塞的进程才可以触发该事件的发生,这是这时就称为这组进程发生死锁。因为没有事件能够被触发,所以死锁是永久性的。
我们生活中常见的死锁问题就是交通死锁。如图所示,显示了一个十字路口4辆车几乎同时到达了,并且相互交叉的停了下来,如果四辆车都要笔直的通过十字路口,那么他们对资源的要求如下:
1车需要象限a和b,
2车需要象限b和c,
3车需要象限c和d,
4车需要象限d和a。
这里写图片描述
这里造成了一种潜在的死锁,因为对每辆车需要前进的资源都能满足,所以仅仅是潜在的死锁。最终只要只有一辆车能前进,就不会死锁。
但是如果每辆车都不管,继续直行,那么每辆车都会占据一个资源,这就会发生真正的死锁。

二 . 死锁的条件

死锁有三个必要条件:

1.互斥。

一次只有一个进程可以使用一个资源,其他进程不能访问分配给其他进程的资源。

2.占有且等待。

当一个进程等待其他进程时,继续占有已经分配的资源。

3.不可抢占。

不能强行抢占进程已有的资源。
在很多情况下这些条件都是很容易达到的。例如,我们所学的消息队列,信号量,互斥是有必要的,因为它保证了数据的完整且正确。
前三个条件都是死锁存在的必要条件,不是充分条件,对死锁的产生还需要第四个条件:

4.循环等待。

存在一个封闭的进程链,使得每个进程至少占有此链中下一个进程所需要的资源。
第四个条件是前三个条件的潜在结果,即假设前三个条件存在,可能发生的一系列会导致发生不可解的循环等待。这实际上就是死锁。前三个条件是死锁的必要条件,第四个条件是死锁的充分条件:
这里写图片描述

三.处理死锁的方法

1)死锁预防。
简单来讲就是设计一种系统来排除死锁的可能性。可以把死锁预防分为两大类:一种间接死锁预防,即防止前面三个条件的任何一个的发生;另一种是直接死锁预防,即防止循环等待的发生。下面我们来具体分析:

互斥:一般来说,地铁一个条件不可能禁止,如果要对资源进行互斥访问,那么系统必须支持互斥,就有可能发生死锁,比如文件,允许多个读访问,但只能允许互斥的写访问,这种情况下,如果多个进程申请权限,就有可能发生死锁。

占有且等待:为预防占有且等待,可要求进程一次性的申请所有的资源,并且阻塞这个进程直到所有请求都得到满足。这个方法有可能很低效,有可能一个进程被阻塞很长时间申请资源,但是其实只需要一部分资源就可以继续执行;另外分配的资源可能长时间不被使用,但也不会被释放,也就不能被其他进程使用;另一个问题就是一个进程不可能事先就知道他所需要的所有资源。

不可抢占:有几种方法可以预防这个条件,如果一个进程继续申请资源被拒绝,那么它要释放最初占用的资源,如果有必要,再次申请;另外如果一个进程申请当前被另一个进程占用的资源,则操作系统可以强行占用另一个系统的资源,要求他释放。

循环等待:可以定义资源的线性顺序来预防。如果一个进程已经分配到了某一类型的资源,那么它只能申请排在这一类型后面的资源。

2)死锁避免

死锁避免允许三个必要的条件,但通过明智的选择,永远不会达到死锁点,这就是死锁避免。它比死锁预防允许更多的并发,例如:如果一个进程请求会导致死锁,则不启动此进程。另如果一个进程申请的资源会导致进程死锁,则不允许分配资源。

3)模拟死锁检测算法

1. 输入:

“资源分配表”文件,每一行包含资源编号、进程编号两项(均用整数表示,并用空格分隔开),记录资源分配给了哪个进程。

“进程等待表”文件,每一行包含进程编号、资源编号两项(均用整数表示,并用空格分隔开),记录进程正在等待哪个资源。

下面是一个示例:

资源分配表:

1 1

2 2

3 3

进程等待表:

1 2

2 3

3 1

2. 处理要求:

程序运行时,首先提示“请输入资源分配表文件的文件名:”;再提示“请输入进程等待表文件的文件名:”。

输入两个文件名后,程序将读入两个文件中的有关数据,并按照死锁检测算法进行检测。

3. 输出要求:

第一行输出检测结果:有死锁 或 无死锁。

第二行输出进程循环等待队列,即进程编号(如果有死锁)。

4. 文件名约定

提交的源程序名字:resourceXXX.c或者resourceXXX.cpp(依据所用语言确定)

输入文件名字:可由用户指定

结果输出到resultXXX.txt中

其中:XXX为账号。

5. 死锁检测算法:当任一进程Pj申请一个已被其他进程占用的资源ri时,进行死锁检测。检测算法通过反复查找进程等待表和资源分配表,

来确定进程Pj对资源ri的请求是否导致形成环路,若是,便确定出现死锁。

6. 测试说明:测试教师将事先准备好一组文件(格式为*.txt),从中为每个程序随机指定一至三个作为输入文件

(被测试者需从键盘输入指定文件的文件名),并查看程序输出结果。

本程序包括:死锁检测算法

#include<stdio.h>

#include<iostream.h>

#include<string.h>

const int MAXQUEUE=100; //定义表的最大行数

typedef struct node{

int resource;

int process;

}cell;

cell occupy[MAXQUEUE];

int occupy_quantity;

cell wait[MAXQUEUE];

int wait_quantity;

//初始化函数

void initial()

{

int i;

for(i=0;i<MAXQUEUE;i++){

occupy[i].process=-1;

occupy[i].resource=-1;

wait[i].process=-1;

wait[i].resource=-1;

}

occupy_quantity=0;

wait_quantity=0;

}

//读数据文件

int readData()

{

FILE *fp;

char fname[20];

int i;

cout<<"请输入资源分配表文件的文件名:"<<endl;

strcpy(fname,"10trouble1.txt");

//cin>>fname;

if((fp=fopen(fname,"r"))==NULL){

cout<<"错误,文件打不开,请检查文件名:)"<<endl;

return 0;

}

else{

while(!feof(fp)){

fscanf(fp,"%d %d",&occupy[occupy_quantity].resource,&occupy[occupy_quantity].process);

occupy_quantity++;

}

}

cout<<"请输入进程等待表文件的文件名:"<<e

ndl;

strcpy(fname,"10trouble2.txt");

//cin>>fname;

if((fp=fopen(fname,"r"))==NULL){

cout<<"错误,文件打不开,请检查文件名:)"<<endl;

return 0;

}

else{

while(!feof(fp)){

fscanf(fp,"%d %d",&wait[wait_quantity].process,&wait[wait_quantity].resource);

wait_quantity++;

}

}

//输出所读入的数据

cout<<endl<<endl<<"输出所读入的数据"<<endl;

cout<<"━━━━━━━━━━━━━━━━━━━━━━━"<<endl;

cout<<"资源分配表"<<endl;

cout<<"资源编号 进程编号"<<endl;

for(i=0;i<occupy_quantity;i++){

cout<<" "<<occupy[i].resource<<" "<<occupy[i].process<<endl;

}

cout<<"───────────────────────"<<endl;

cout<<"进程等待表"<<endl;

cout<<"进程编号 资源编号"<<endl;

for(i=0;i<wait_quantity;i++){

cout<<" "<<wait[i].resource<<" "<<wait[i].process<<endl;

}

return 1;

}

//检测

void check()

{

int table[MAXQUEUE][MAXQUEUE];

int table1[MAXQUEUE][MAXQUEUE];

int i,j,k;

int flag,t,p;

int max_process;

//初始化表格

for(i=0;i<MAXQUEUE;i++){

for(j=0;j<MAXQUEUE;j++){

table[i][j]=0;

table1[i][j]=0;

}

}

//先找到进程最大编号

max_process=-1;

for(i=0;i<occupy_quantity;i++){

if(occupy[i].process>max_process){

max_process=occupy[i].process;

}

}

for(i=0;i<wait_quantity;i++){

if(wait[i].process>max_process){

max_process=wait[i].process;

}

}

for(i=0;i<wait_quantity;i++){

for(j=0;j<occupy_quantity;j++){

if(wait[i].resource==occupy[j].resource){

table[wait[i].process][occupy[j].process]=1;

table1[wait[i].process][occupy[j].process]=1;

}

}

}

cout<<"初始等待占用表:"<<endl;

for(i=0;i<max_process+1;i++){

for(j=0;j<max_process+1;j++){

cout<<table[i][j]<<" ";

}

cout<<endl;

}

cout<<endl;

for(i=0;i<max_process+1;i++){

for(j=0;j<max_process+1;j++){

for(k=0;k<max_process+1;k++){

table[i][j]=table[i][j]||(table[i][k]&&table[k][j]);

}

}

}

cout<<"检测后的等待占用表:"<<endl;

for(i=0;i<max_process+1;i++){

for(j=0;j<max_process+1;j++){

cout<<table[i][j]<<" ";

}

cout<<endl;

}

flag=-1;

for(i=0;i<max_process+1;i++){

if(table[i][i]==1){

flag=i;

break;

}

}

cout<<endl<<endl<<"检测结果"<<endl;

cout<<"───────────────────"<<endl;

if(flag!=-1){

cout<<"存在死锁"<<endl;

cout<<"进程循环等待队列:";

p=flag; //存在进程循环等待队列的那一进程

//进程循环等待队列中的所有进程是table表中的这一行是1的进程,只是顺序要再确定

t=1;

while(t){

cout<<p<<" ";

for(j=0;j<max_process+1;j++){

if(table1[p][j]==1){

if(table[j][flag]==1){

p=j;

break;

}

}

}

if(p==flag)t=0;

}

cout<<flag<<endl;

}

else{

    cout<<"不存在死锁"<<endl;

}

}


void main()

{

    int flag;

    version();

    initial();

    flag=readData();

if(flag)check();

}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值