线程同步、死锁以及银行家算法模拟小例子

//首先是摘自孙鑫老师vc++里的一些典型例子,然后是模拟了一下银行家算法

1.使用互斥对象进行线程同步

#include <Windows.h>
#include <iostream>
using namespace std;
//使用互斥对象进行线程同步
DWORD WINAPI Thread1(LPVOID lpParameter);
DWORD WINAPI Thread2(LPVOID lpParameter);
int index=0;
int tickets=100;
HANDLE hMutex;
void main(int argc,char *argv[])
{
	HANDLE hThread1,hThread2;
	//创建互斥对象
	hMutex=CreateMutex(NULL,TRUE,NULL);//FALSE表示没有线程拥有这个互斥对象,操作系统将互斥对象设为有信号状态
	//创建线程
	hThread1=CreateThread(NULL,0,Thread1,NULL,0,NULL);
	hThread2=CreateThread(NULL,0,Thread2,NULL,0,NULL);
	CloseHandle(hThread1);
	CloseHandle(hThread2);
	ReleaseMutex(hMutex);
	Sleep(4000);
}
DWORD WINAPI Thread1(LPVOID lpParameter)
{
	while (1)
	{
		WaitForSingleObject(hMutex,INFINITE);//等待的互斥对象hMutex变为有信号状态才往下执行
		if (tickets>0)
		{
			Sleep(1);//人为切换线程
			cout<<"Thread1 selling ticket: "<<tickets--<<endl;
		}
		else
			break;
		ReleaseMutex(hMutex);
	}
	return 0;
}

DWORD WINAPI Thread2(LPVOID lpParameter)
{
	while (1)
	{
		WaitForSingleObject(hMutex,INFINITE);//等待的互斥对象hMutex变为有信号状态才往下执行
		if (tickets>0)
		{
			Sleep(1);
			cout<<"Thread2 selling ticket: "<<tickets--<<endl;
		}
		else
			break;
		ReleaseMutex(hMutex);
	}
	return 0;
}


2.使用事件对象进行线程同步

DWORD WINAPI Thread1(LPVOID lpParameter);
DWORD WINAPI Thread2(LPVOID lpParameter);
int index=0;
int tickets=100;
HANDLE g_hEvent;
int main(int argc,char *argv[])
{
	HANDLE hThread1,hThread2;
	//创建自动重置事件内核对象
	g_hEvent=CreateEvent(NULL,FALSE,FALSE,NULL);
	SetEvent(g_hEvent);//把事件对象设为有信号状态 
	//创建线程
	hThread1=CreateThread(NULL,0,Thread1,NULL,0,NULL);
	hThread2=CreateThread(NULL,0,Thread2,NULL,0,NULL);
	CloseHandle(hThread1);
	CloseHandle(hThread2);
	Sleep(4000);
	CloseHandle(g_hEvent);
}


DWORD WINAPI Thread1(LPVOID lpParameter)
{
	while (1)
	{
		//请求事件对象
		WaitForSingleObject(g_hEvent,INFINITE);
		//请求到事件后,操作系统会自动把事件对象设置为无信号状态
		if (tickets>0)
		{
			Sleep(1);//人为切换线程
			cout<<"Thread1 selling ticket: "<<tickets--<<endl;
			SetEvent(g_hEvent);//调用SetEvent把事件对象设置为有信号状态
		}
		else
		{
			SetEvent(g_hEvent);//调用SetEvent把事件对象设置为有信号状态
			break;
		}
	}
	return 0;
}

DWORD WINAPI Thread2(LPVOID lpParameter)
{
	while (1)
	{
		//请求事件对象
		WaitForSingleObject(g_hEvent,INFINITE);
		if (tickets>0)
		{
			Sleep(1);
			cout<<"Thread2 selling ticket: "<<tickets--<<endl;
			SetEvent(g_hEvent);
		}
		else
		{
			SetEvent(g_hEvent);
			break;
		}
	}
	return 0;
}

3.使用关键代码段实现线程同步

DWORD WINAPI Thread1(LPVOID lpParameter);
DWORD WINAPI Thread2(LPVOID lpParameter);
int index=0;
int tickets=100;
CRITICAL_SECTION g_cs;
int main(int argc,char *argv[])
{
	HANDLE hThread1,hThread2;
	//创建线程
	hThread1=CreateThread(NULL,0,Thread1,NULL,0,NULL);
	hThread2=CreateThread(NULL,0,Thread2,NULL,0,NULL);
	CloseHandle(hThread1);
	CloseHandle(hThread2);
	InitializeCriticalSection(&g_cs);//初始化关键代码段(建立电话亭)
	Sleep(4000);
	DeleteCriticalSection(&g_cs);//释放临界区对象所有资源(拆除电话亭)
}


DWORD WINAPI Thread1(LPVOID lpParameter)
{
	while (1)
	{
		EnterCriticalSection(&g_cs);//请求临界区对象所有权(进入电话亭)
		if (tickets>0)
		{
			Sleep(1);//人为切换线程
			cout<<"Thread1 selling ticket: "<<tickets--<<endl;
			LeaveCriticalSection(&g_cs);//释放临界区对象所有权(离开电话亭)
		}
		else
		{
			LeaveCriticalSection(&g_cs);//释放临界区对象所有权(离开电话亭)
			break;
		}
	}
	return 0;
}

DWORD WINAPI Thread2(LPVOID lpParameter)
{
	while (1)
	{
		EnterCriticalSection(&g_cs);//请求临界区对象所有权(进入电话亭)
		if (tickets>0)
		{
			Sleep(1);//人为切换线程
			cout<<"Thread2 selling ticket: "<<tickets--<<endl;
			LeaveCriticalSection(&g_cs);//释放临界区对象所有权(离开电话亭)
		}
		else
		{
			LeaveCriticalSection(&g_cs);//释放临界区对象所有权(离开电话亭)
			break;
		}
	}
	return 0;
}

//线程死锁问题:

//线程1拥有临界区对象A,等待临界区对象B的所有权;
//线程2拥有临界区对象B,等待临界区对象A的所有权;

DWORD WINAPI Thread1(LPVOID lpParameter);
DWORD WINAPI Thread2(LPVOID lpParameter);
int index=0;
int tickets=100;
CRITICAL_SECTION g_csA;
CRITICAL_SECTION g_csB;
int main(int argc,char *argv[])
{
	HANDLE hThread1,hThread2;
	//创建线程
	hThread1=CreateThread(NULL,0,Thread1,NULL,0,NULL);
	hThread2=CreateThread(NULL,0,Thread2,NULL,0,NULL);
	CloseHandle(hThread1);
	CloseHandle(hThread2);
	InitializeCriticalSection(&g_csA);
	InitializeCriticalSection(&g_csB);
	Sleep(4000);
	DeleteCriticalSection(&g_csA);
	DeleteCriticalSection(&g_csB);
}


DWORD WINAPI Thread1(LPVOID lpParameter)
{
	while (1)
	{
		EnterCriticalSection(&g_csA);//请求临界区对象A的所有权
		Sleep(1);
		EnterCriticalSection(&g_csB);//请求临界区对象A的所有权
		if (tickets>0)
		{
			Sleep(1);
			cout<<"Thread1 selling ticket: "<<tickets--<<endl;
			LeaveCriticalSection(&g_csB);//先释放临界区对象B所有权再释放临界区对象A的所有权(与请求顺序相反)
			LeaveCriticalSection(&g_csA);
		}
		else
		{
			LeaveCriticalSection(&g_csB);
			LeaveCriticalSection(&g_csA);
			break;
		}
	}
	return 0;
}

DWORD WINAPI Thread2(LPVOID lpParameter)
{
	while (1)
	{
		EnterCriticalSection(&g_csB);//请求临界区对象A的所有权
		Sleep(1);
		EnterCriticalSection(&g_csA);//请求临界区对象A的所有权
		if (tickets>0)
		{
			Sleep(1);
			cout<<"Thread2 selling ticket: "<<tickets--<<endl;
			LeaveCriticalSection(&g_csA);//与请求顺序相反
			LeaveCriticalSection(&g_csB);
		}
		else
		{
			LeaveCriticalSection(&g_csA);
			LeaveCriticalSection(&g_csB);
			break;
		}
	}
	return 0;
}

/*
Analysis:
线程1得到临界区对象g_csA所有权后,调用sleep函数,线程1睡眠1ms,放弃执行机会;
操作系统选择线程2执行,线程2首先等待临界区对象g_csB的所有权,当它得到所有权
后,调用sleep,线程2也睡眠1ms;于是轮到线程1执行,它此时等待临界区对象g_csB
的所有权,但是g_csB已经被线程2所拥有,因此线程1继续等待;线程1等待时,线程2
开始执行,这时它需要临界区资源g_csA,然而g_csA被A所拥有,此时线程2进入等待;
这就导致了线程1和线程2发生死锁;
线程同步三种方式对比:
(1)互斥对象和事件对象属于内核对象,利用内核对象进行线程同步时,速度较慢,但是
利用内核对象可以在多个进程的各个线程中进行同步
(2)关键代码段工作在用户方式,同步速度快,但在使用时,很容易进入死锁状态,因为
在等待进入关键代码段时无法设定超时值。
*/

//银行家算法-解决死锁

/*
Copyright Keiko,2014. All rights reserved.
银行家算法:
银行在发放贷款之前都会预测贷款是否会导致资金周转问题出现。
1.可用资源向量Available
具有m个元素的数组,每个元素代表一类可利用的资源的数目,初始
值为系统中该资源的最大可用数目,其值随着该类资源的分配回收而
动态地改变。Avaliable[j]=k,可用的Rj类资源有k个
2.最大需求矩阵MAX
n*m矩阵,定义系统中n个进程中每一个进程对m类资源的最大需求。
MAX[i][j]=k,进程i需要Rj类资源k个
3.分配矩阵Allocation
n*m矩阵,Allocation[i][j]=k,进程i已分得Rj类资源k个
4.剩余需求量矩阵Need
n*m矩阵,每一个进程已经剩余需要的资源个数
Need[i][j]=k,进程i还需要资源k个
算法:
设进程ri提出请求request[m],则银行家算法按如下规则进行判断。
(1)如果request[m]<=need[n][m],则转(2);否则,出错。
(2)如果request[m]<=available[m],则转(3);否则,出错。
(3)系统试探分配资源,修改相关数据:
available[m]= available[m]-request[m]
allocation[ri][m]=allocation[ri][m]+request[m]
need[ri][m]=need[ri][m]-request[m]
其中,pn指第pn行申请资源。
(4)系统执行安全性检查,如安全,则分配成立;否则试探险性分配作废,系统恢复原状,进程等待。
安全检查
(1)设置两个工作向量work=available;finish[n]=0;
(2)从进程集合中找到一个满足下述条件的进程,
finish[i]=0
need<=work
如找到,执行(3);否则,执行(4)
(3)设进程获得资源,可顺利执行,直至完成,从而释放资源:
work=work+allocation;finish[i]=1;转(2);
(4)如所有的进程finish[p]=1,则表示安全;否则系统不安全。
*/
#include <iostream>
using namespace std;
#define n 4 //进程个数
#define m 3 //资源个数


int Available[m];
int MAX[n][m];
int Allocation[n][m];
int Need[n][m];


void Initialze()
{
	cout<<"请输入可用资源向量"<<endl;
	for (int i=0;i<m;i++)
	{
		cin>>Available[i];
	}
	cout<<"请输入最大需求矩阵MAX"<<endl;
	for (int i=0;i<n;i++)
	{
		for (int j=0;j<m;j++)
		{
			cin>>MAX[i][j];
		}
	}
	cout<<"请输入分配矩阵Allocation"<<endl;
	for (int i=0;i<n;i++)
	{
		for (int j=0;j<m;j++)
		{
			cin>>Allocation[i][j];
		}
	}
	cout<<"剩余需求量矩阵Need"<<endl;
	for (int i=0;i<n;i++)
	{
		for (int j=0;j<m;j++)
		{
			cin>>Need[i][j];
		}
	}


}
bool Compare(int need[m],int avail[m])//need<=available,return true
{
	for (int i=0;i<m;i++)
	{
		if (need[i]>avail[i])
		{
			return false;
		}
	}
	return true;
}


bool IsSafety(int allocation[][m],int need[][m],int available[])
{
	int finish[n];
	int need_col[m];
	int available_col[m];
	memset(finish,0,sizeof(finish));//finish[i]=1表示第i个进程可以实现
	memcpy(available_col,available,sizeof(available_col));
	for (int i=0;i<n;i++)
	{
		for (int j=0;j<n;j++)//轮询状态是否可以使得全部进程满足
		{
			if (finish[j]==1)//第j个进程已经完成,继续下一趟轮询
			{
				continue;
			}
			else
			{
				memcpy(need_col,need[j],sizeof(need[j]));
				if (Compare(need_col,available_col)==true)
				{
					finish[j]=1;
					//进程完成,释放所有资源
					for (int k=0;k<m;k++)
					{
						available_col[k]+=allocation[j][k];
					}
					break;
				}
			}
		}
	}
	for (int i=0;i<n;i++)
	{
		if (finish[i]!=1)
		{
			return false;
		}
	}
	return true;
}




//#define n 4 //进程个数
//#define m 3 //资源个数
bool Banker(int allocation[][m],int need[][m],int available[m],int request[m],int ri)
{
	int index=ri-1;
	int need_col[m];
	memcpy(need_col,need[index],sizeof(need[index]));
	//如果request[]<=need[index][],则转(2);否则出错
	//如果request[]<=available[],则转(3);否则出错
	if (Compare(request,need_col)&&Compare(request,available))
	{
		//试探性分配资源
		for (int i=0;i<m;i++)
		{
			allocation[index][i]+=request[i];
			need[index][i]-=request[i];
			available[i]-=request[i];
		}
		//进行安全性检查
		if (IsSafety(allocation,need,available))
		{
			cout<<"允许第"<<ri<<"个进程申请资源"<<endl;
		}
		else
		{
			cout<<"不允许第"<<ri<<"个进程申请资源"<<endl;
			//恢复状态
			for (int i=0;i<m;i++)
			{
				allocation[index][i]-=request[i];
				need[index][i]+=request[i];
				available[i]+=request[i];
			}
		}
		return true;
	}
	else
	{
		cout<<"第"<<ri<<"个进程申请资源出错"<<endl;
		return false;
	}
}




int main(int argc,char *argv[])
{
	Initialze();
	if (IsSafety(Allocation,Need,Available))//判断初始状态是否安全)
	{
		cout<<"初始状态安全"<<endl;
	}
	else
	{
		cout<<"初始状态不安全"<<endl;
		return -1;
	}
	int request[m]={0};
	int ri;
	cout<<"申请资源的进程号为:"<<endl;
	cin>>ri;
	cout<<"申请的资源向量为:"<<endl;
	for (int i=0;i<m;i++)
	{
		cin>>request[i];
	}
	Banker(Allocation,Need,Available,request,ri);
}


/*
请输入可用资源向量
0 1 1
请输入最大需求矩阵MAX
3 2 2
6 1 3
3 1 4
4 2 2
请输入分配矩阵Allocation
1 0 0
6 1 2
2 1 1
0 0 2
剩余需求量矩阵Need
2 2 2
0 0 1
1 0 3
4 2 0
初始状态安全
申请资源的进程号为:
2
申请的资源向量为:
0 0 1
允许第2个进程申请资源
========================
申请资源的进程号为:
1
申请的资源向量为:
0 0 1
不允许第1个进程申请资源
========================
申请资源的进程号为:
4
申请的资源向量为:
1 2 0
第4个进程申请资源出错
========================
初始状态安全
申请资源的进程号为:
4
申请的资源向量为:
0 1 0
允许第4个进程申请资源
*/


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值