死锁及避免死锁的银行家算法

Linux下:

互斥锁是保护临界资源被线程间(或进程间)互斥的访问临界资源,当一个线程得到锁不释放时另一个线程申请时必须等待,由此可以得到概念:

死锁:(两种情况)

(1)同一个线程先后两次调用lock,在第二次调用时,由于锁已经被自己占用,该线程会挂起等待自己释放锁,由于该线程已被挂起而没有机会释放锁,因此 它将一直处于挂起等待状态,变为死锁;

(2)线程A获得了锁1,线程B获得了锁2,这时线程A调用lock试图获得锁2,结果是需要挂起等待线程B释放锁2,而这时线程B也调用lock试图获得锁1,结果是需要挂起等待线程A释放锁1,于是线程A和B都在等待对方释放自己才释放,从而造成两个都永远处于挂起状态,造成死锁。

总结:死锁是指多个并发线程在运行过程中因竞争资源而造成的一种僵局,当线程间处于这种僵持状态下,若无外力作用,这些线程都将无法推进。


所以产生死锁的条件主要有以下几个原因:

(1)系统资源不足:系统中所拥有的资源其数量不足以满足线程运行的需要,使得在运行过程中,因争夺资源而陷入僵局;

(2)线程间推进的顺序不当:线程间在运行过程中,申请和释放的顺序不合法:

例如:以上死锁(2)中由于两线程都先申请获得锁,然后又互相申请对方获得的锁才释放自己的,从而造成死锁,就是由于申请顺序不当造成的.

解决办法:如果所有线程在需要多个锁时都按相同的先后顺序(常见的是按Mutex变量的地址顺序)获得锁,则不会出现死 锁。如一个程序中用到锁1、锁2、锁3,它们所对应的Mutex变量的地址是锁1<锁2<锁3,那么所有线程在需要同时获得2个或3个锁时都应该按锁1、锁2、锁3的顺序获得(要为所有的锁确定一个先后顺序有时比较困难,则应该尽量使用pthread_mutex_trylock函数代替pthread_mutex_lock 调用,使其不阻塞等待,以免死锁).

(3)资源分配不当.


产生死锁所必须同时具备的四个必要条件:

(1)互斥条件:

即在一段时间内,某资源只能被一个线程所占用,若此时其它线程申请则必须等待,直至当前线程用完释放;

(2)请求和保持条件:

线程已拥有一个资源,此时它又申请新的资源,若新资源被其它线程占用,当前线程则会阻塞等待,但其会保持不放自己获得的资源;

(3)不可抢占条件:

线程在已获得的资源未使用完之前不能被抢占,只能自己使用完后自己释放;

(4)循环等待条件:

在发生死锁时,必然存在一个循环链,即线程集合{T0,T1,T2,...,Tn}中T0正在等待T1占用的资源,T1正在等待T2占用的资源,.........,Tn正在等待Tn占用的资源。

处理死锁的方法:

(1)预防死锁:可破坏请求与保持条件,破坏不可抢占条件,破坏循环等待条件,但不可以破坏互斥条件,线程间访问临界资源必须互斥访问;

(2)避免死锁:在资源动态分配过程中,用某种方法防止系统进入不安全状态,从而避免死锁发生;

(3)检测死锁:线程发生死锁,此时通过检测机构及时检测死锁发生,采取适当措施,将其从死锁中解脱出来;

(4)解除死锁:检测到死锁,将进程从死锁状态中解脱出来,常用方法为撤消一些进程,回收它们资源,将它们分配给已阻塞的状态线程,使其能继续运行。


此处介绍避免死锁的银行家算法:

每一个线程进入系统时,它必须声明在运行过程中,所需的每种资源类型最大数目,其数目不应超过系统所拥有每种资源总量,当线程请求一组资源系统必须确定有足够资源分配给该进程,若有在进一步计算这些资源分配给进程后,是否会使系统处于不安全状态,不会(即若能在分配资源时找到一个安全序列),则将资源分配给它,否则等待。

具体介绍:

假定系统中有五个线程{P0,P1,P2,P3,P4}和三类资源{A,B,C},各类资源数量分别为10,5,7,在T0时刻分配资源情况如图:

Max:表示线程对每类资源的最大需求量;

Allocation:表示系统给线程已分配每类资源的数目;

Need:表示线程还需各类资源数目;

Available:表示系统当前剩下的资源。


从初始找出安全序列:

(1)首先系统剩下资源{3,3,2},查表可满足5个进程Need的进程有:P1(1,2,2)、P3(0,1,1),先给P1分配;

(2)P1分配以后执行完释放其所占资源后系统此时剩下资源有:Allocation+{3,3,2}={5,3,2};

(3)根据系统剩下资源查表可满足剩下4个进程Need的进程有P3{0,1,1}、P4{4,3,1},再给P3分配;

(4)P3分配以后执行完释放其所占资源后系统此时剩下资源有:Allocation+{5,3,2}={7,4,3};

(5)根据系统剩下资源查表可满足剩下3个进程Need的进程有P0{7,4,3}、P2{6,0,0}、P4{4,3,1},再给P4分配;

(6)P4分配以后执行完释放其所占资源后系统此时剩下资源有:Allocation+{7,4,3}={7,4,5};

(7)根据系统剩下资源查表可满足剩下2个进程Need的进程有P0{7,4,3}、P2{6,0,0},再给P2分配;

(8)P2分配以后执行完释放其所占资源后系统此时剩下资源有:Allocation+{7,4,5}={10,4,7};

(9)根据系统剩下资源查表可满足剩下1个进程Need的进程有P0{7,4,3},最后给P0分配;

(10)P0分配以后执行完释放其所占资源后系统此时剩下资源有:Allocation+{10,4,7}={10,5,7};

(11)所有进程按此序列{P1,P3,P4,P2,P0}可安全执行完毕,最后系统资源全部释放。(由以上也可知安全序列不唯一,但只要找出一个安全序列,说明此系统是安全的(找到安全序列可按此序列真正执行进程推进顺序,若没找到,则恢复初始状态,其并没有真正给进程分配资源,只是提前避免))

由表表示:

work:表示系统当前剩下的资源数;


代码实现:

#include <iostream>
#include <cstdlib>
#include <vector>
#include <assert.h>
using namespace std;

#define MAX 20

enum Tag
{
	False,
	True
};
class Banker
{
public:
	Banker(int n,int r)
		:I(n)
		,R(r)
	{}
	void Init()  //初始化进程资源
	{
		cout<<"请输入每种资源的总数:"<<endl;
		for(int i=0;i<R;++i)
		{
			cin>>memery[i];
		}
		cout<<endl;
		for(int i = 0; i < I; ++i)
		{
			cout<<"P"<<i<<"(Max):"<<" ";
			for(int j=0;j<R;++j)
			{ 
			    cin>>Max[i][j];
			}
			cout<<"P"<<i<<"(Allocation):"<<" ";
			for(int j=0;j<R;++j)
			{ 
			    cin>>Allocation[i][j];
			}
			for(int j=0;j<R;++j)
			{ 
				//计算各进程还需要的资源数
			    Need[i][j]=Max[i][j]-Allocation[i][j];
			}
		}
		for(int i=0;i<R;++i)
		{
			int sum[MAX]={0};
			for(int j=0;j<I;++j)
			{
				if(i==0)
					sum[i]+=Allocation[j][i];
				if(i==1)
					sum[i]+=Allocation[j][i];
				if(i==2)
					sum[i]+=Allocation[j][i];
			}
			Available[i]=memery[i]-sum[i]; //总的资源数-已分配的资源数
		}
		cout<<endl;
	}
	void Display()        //打印进程现在时刻资源分配情况
	{
		cout<<endl<<"现在时刻的状态:"<<endl;
		printf("进程\t Max\t Allocation\t Need\t Available\n");

		for(int i = 0; i < I; ++i)
		{
			printf("P%d\t",i);
			for(int j = 0; j < R; ++j)
			{
				cout<<Max[i][j]<<" ";
			}
			printf("\t   ");
			for(int j = 0; j < R; ++j)
			{
				cout<<Allocation[i][j]<<" ";
			}
			printf("\t");
			for(int j = 0; j < R; ++j)
			{
				cout<<Need[i][j]<<" ";
			}
			printf("\t  ");
			for(int j = 0; j < R; ++j)
			{
				if(i == 0)
					cout<<Available[j]<<" ";
				else
					cout<<" "<<" ";
			}
			cout<<endl;
		}
	}
	void Safe()   //寻找安全序列
	{
		int work[MAX] = {0};
		memcpy(work,Available,R*sizeof(int));  //将可利用的资源Available数组内容给work数组
		_FindSafe(work);   //查找安全序列并打印
		SafeNum.clear();//寻找安全序列后清空容器
	}

	//请求资源寻找安全序列
	void Request(int id,int* req)
	{
		assert(id>=0&&id<=I);
		int work[MAX] = {0};
		int need[MAX] = {0};
		int Allocate[MAX] = {0};
		int Av[MAX] = {0};

		for(int i = 0; i < R; ++i)  //判断某进程申请的资源是否是否小于等于其需要的或系统现所拥有的
		{
			if(req[i] > Need[id][i])
			{
				cout<<"警告:超过此进程需求!"<<endl;
				return;
			}
			if(req[i] > Available[i])
			{
				 cout<<"警告:申请量超过当前可用资源数!"<<endl;
				 return;
			}
		}
		//保存此进程原所需求资源、已分配资源和系统可利用资源Available
		memcpy(need,Need[id],R*sizeof(int));
		memcpy(Allocate,Allocation[id],R*sizeof(int));
		memcpy(Av,Available,R*sizeof(int));

		//修改Allocation、Need和Available变量,变为现在状态下此进程各项资源
		_Add(Allocation[id],req,R);
		_Sub(Need[id],req,R);
		_Sub(Available,req,R);

		memcpy(work,Available,R*sizeof(int));
		_FindSafe(work);//此时状态下寻找安全序列
		if(SafeNum.size()==I)
		{
			cout<<"找到安全序列,为P"<<id<<"分配资源......";
			cout<<endl;
		}
        //没有找到则改变回原状态
		else
		{
			memcpy(Need[id],need,R*sizeof(int));
		    memcpy(Allocation[id],Allocate,R*sizeof(int));
		    memcpy(Available,Av,R*sizeof(int));
		}
		SafeNum.clear();//寻找安全序列后清空容器
	}
protected:
	void _Sub(int* arr1, int* arr2,int n)
	{
		while(n--)
		{
			*arr1 = *arr1 - *arr2;
			arr1++;
			arr2++;
		}
	}

	void _Add(int* arr1, int* arr2,int n)
	{
		while(n--)
		{
			*arr1 = *arr1+ *arr2;
			arr1++;
			arr2++;
		}
	}
	
	bool _Compare(int* arr1,int* arr2)
	{
		for(int i = 0; i < R; ++i)
		{
			if(arr1[i] > arr2[i])
				return false;
		}
		return true;
	}

	void _FindSafe(int* work)   //查找安全序列
	{
		int WorkCur[MAX][MAX] = {0};
		vector<int> finish(I,0);//表示系统是否有足够资源分配给系统
		int WorkPrev[MAX][MAX] = {0};

		int j = 0; 
		int i = 0;
		for(j=0;j<I;++j)
		{
			for(i=0;i<I;++i)
			{
			    if(finish[i]== False)
			    {
					//判断是否可以满足需求
					if(_Compare(Need[i],work))//比较现有资源与进程需求
					{
						SafeNum.push_back(i); //记下进程号
						finish[i] = True;    //状态标记为已完成;
                        //更新数据
						_Add(WorkPrev[i],work,R);  //保存没分配前系统所有的资源,即将work中的资源数给WorkMartix
						_Add(work,Allocation[i],R);  //计算系统现在可利用资源数work==前work+Allocation
						_Add(WorkCur[i],work,R); //前work+Allocation
					}
				}
			}
			if(SafeNum.size() == I)//表示进程已被分配完,找到安全序列
					break;
			if(i==I&&SafeNum.size()==0)//若比较一次完成后容器没有符合要求的进程直接退出
				break;
		}
		if(SafeNum.size() != I)  //若进程个数没有全部入进vector容器中,则没找到安全序列
		{
			cout<<"!!!试分配未发现安全序列,系统不分配资源!!!"<<endl;
			return;
		}
		_Print(WorkCur,finish,WorkPrev);
	}

	//打印安全序列
	void _Print(int WorkCur[][MAX],vector<int> finish,int WorkPrev[][MAX])
	{
		printf("\n安全序列为: ");
		for(int i = 0; i < I; ++i)
		{
			printf("P[%d] ",SafeNum[i]);
		}
		cout<<endl;
		printf("进程\t work\t need\t allocation\t work + allocation\t Finish\n");
		for(int i = 0; i < I; ++i)
		{
			int id = SafeNum[i];
			printf("P%d\t",SafeNum[i]);
			for(int j = 0; j < R; ++j)
			{
				cout<<WorkPrev[id][j]<<" ";
			}
			printf("\t ");
			for(int j = 0; j < R; ++j)
			{
				cout<<Need[id][j]<<" ";
			}
			printf("\t  ");
			for(int j = 0; j < R; ++j)
			{
				cout<<Allocation[id][j]<<" ";
			}
			printf("\t\t");
			for(int j = 0; j < R; ++j)
			{
				cout<<WorkCur[id][j]<<" ";
			}
			printf("\t\t  ");
			cout<<finish[id]<<endl;
		}
		cout<<endl;
	}

protected:
	int I;  //代表有几个进程
	int R;  //代表几类资源
	int memery[MAX];  //资源的总数
	int Max[MAX][MAX];  //对各类资源最大需求量
	int Allocation[MAX][MAX];  //每个进程当前已分配各类资源数
	int Need[MAX][MAX];   //每个进程当前的需求量
	int Available[MAX];  //系统当前剩余可分配的各类资源
	vector<int> SafeNum;  //记录安全序列
};

void Menu()
{
	cout<<endl;
    cout<<"****************** 银行家算法 *****************"<<endl;
	cout<<"************ 1. 初始化进程资源情况 ************"<<endl;
    cout<<"************ 2. 查看现在时刻资源分配情况 ******"<<endl;
    cout<<"************ 3. 查看现在时刻是否是安全状态 ****"<<endl;
    cout<<"************ 4. 为进程申请资源 ****************"<<endl;
    cout<<"************ 0. 退出 **************************"<<endl;
	cout<<endl;
}
void test()
{
	cout<<"请输入进程个数和资源种类:";
	int M,N;
	cin>>M>>N;
    Banker b(M,N);
	while(1)
    {
		Menu();
        cout<<"请选择:";
		int ch = 0;
        cin>>ch;
        switch (ch)
        {
        case 0:
            exit(0);
            break;
		case 1:
			b.Init();
            break;
        case 2:
            b.Display();
            break;
        case 3:
            b.Safe();
            break;
        case 4:
			{
				int id = 0;
				int request[MAX] = {0};
				cout<<"请输入要申请的进程号:";
				cin>>id;
				cout<<"请输入各类资源个数:";
				for(int i = 0; i < N; ++i)
					cin>>request[i];
				b.Request(id,request);
			}
            break;
        default:
            cout<<"请重新输入!!!"<<endl;
            break;
        }
    }
}
int main()
{
	test();
	system("pause");
	return 0;
}





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章