死鎖及避免死鎖的銀行家算法

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;
}





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