多线程编程实现银行家算法(g++11新版本,解决了CSDN相关内容比较陈旧的问题)

本文详细介绍了如何使用C++11的多线程库``、``和``来实现银行家算法。通过多线程模拟进程申请和释放资源,确保系统安全。文章涵盖了实验内容、实验目的、设计思路、数据结构和算法实现,并提供了源代码结构和主要函数的解释。
摘要由CSDN通过智能技术生成

        CSDN上有关多线程编程的文章很多,内容也很奈斯,但是内容有些陈旧,现今的g++11版本推出了多线程编程的新库<thread>,还有和多线程编程搭配的种种库比如<condition_variable>、<mutex> 等等,如何使用这些新库来编写多线程实现银行家算法呢,笔者在这篇文章中会尽量全面地向大家展示详细过程与细节。

目录

一、实验内容

二、实验目的

三、设计思路和流程图

(一)银行家算法的实现

概念:

在实现银行家算法时,涉及到一些数据结构,在此简要说明:

整体实现框架描述

1、安全性算法思路

2、资源请求算法思路

(二)多线程程序的实现

新版本的多线程编程大概有以下操作:

回调函数主要实现以下功能:

主函数实现:

四、源程序与相关注释

1、整体函数结构

2、库及命名空间引用

3、定义全局变量 

4、功能函数详解

Ⅰ、LessOrNot

Ⅱ、PlusMatrix

Ⅲ、SubMatrix

Ⅳ、Release

5、初始化函数详解

6、显示函数详解

7、安全性算法详解

8、资源分配算法详解(内部会链接安全性算法)

9、多线程回调函数

10、Main函数

五、总结


一、实验内容

编写一个多线程程序,实现银行家算法。创建n个线程来向银行申请或者释放资源。只有保证系统安全,银行家才会批准请求。使用C++11版本进行编译运行。

二、实验目的

  • 编程银行家算法,对银行家算法中有关安全性和分配算法的实现搭建清晰的框架,理解死锁的产生机理,深入了解银行家算法的实现机制。
  • 采用多线程进行编程,进一步加深对多线程的理解,明晰多线程的作用机制以及运行时需要注意的问题,并与父子进程进行类比和对比,提升对多线程和父子进程的理解。

三、设计思路和流程图

在实现该实验相关内容时,我们可以将整体代码分为两个大框架,一个框架用于实现银行家算法,另一个框架用于实现多线程操作,接下来我们也将分别从这两个框架进行详细说明。

(一)银行家算法的实现

概念

对于每种资源类型有多个实例的资源分配系统来说,资源分配图算法就不适用了。因此我们采用银行家算法这一死锁避免算法来适配该类型的系统(效率比资源分配图方案要低)。该算法如此命名是因为它可用于银行系统,当它不能满足所有客户的需要时,银行绝不会分配其现金。

在实现银行家算法时,涉及到一些数据结构,在此简要说明:

1、Availabe:长度为m的向量,表示每种资源的现有实例的数量。如果Availabe[j] = k,那么资源类型Rj现有k个实例。

2、Max:n * m 矩阵,定义每个进程的最大需求。如果Max[i][j] = k,那么进程Pi最多可申请k个资源类型Rj的实例。

3、Allocation:n * m矩阵,定义每个进程现在所分配的各种资源类型的实例数量。如果Allocation[i][j] = k,那么进程Pi现在已分配了k个资源类型Rj的实例。

4、Need:n * m矩阵,表示每个进程还需要的剩余的资源。如果Need[i][j] = k,那么进程Pi还可能申请k个资源类型Rj的实例。注意,Need[i][j] = Max[i][j] – Allocation[i][j]。

5、request:长度为m的向量,表示某一进程所请求的资源的数量。如果request[j] = k,那么说明该进程请求k个Rj资源。

以上数据结构,在本实验程序中都将被定义为全局变量。

整体实现框架描述

在实现银行家算法时,我们主要需要实现几个功能,即

·安全性算法(确定计算机是否处于安全状态的算法)

·资源请求算法(判断是否可安全允许请求的算法)

·初始化算法(给各个数据结构以及初始化变量赋值)

·显示操作(将目前的系统情况各资源情况显示在交互屏上)

各个算法之间具有一定的联系,银行家算法框架借助以上功能即可顺利实现,大致流程图如下图所示:

 

在此对安全性算法和资源请求算法做简要叙述:

1、安全性算法思路

Ⅰ、设Work和Finish分别为长度为m和n的向量。按如下方式进行初始化,Work = Available且对于i = 0, 1, … ,n-1, Finish[i] = false。

Ⅱ、查找这样的i使其满足:

a.Finish[i] = false

b.Needi <=Work

如果没有这样的i存在,那么就跳转到Ⅳ。

Ⅲ、Work = Work + Allocationi

Finish[i] = true

返回到Ⅱ。

Ⅳ、如果对于所有的i,都有Finish[i] = true,那么系统处于安全状态。

简而言之,就是说对于系统的当前状态,我们能否找到一个合理的顺序完成资源的分配和回收,如果能,就说明系统是安全的,否则系统就是不安全的。

2、资源请求算法思路

设Requesti为进程Pi的请求向量。如果Requesti[j] == k,那么进程Pi需要资源类型Rj的实例数量为k。当进程Pi作出资源请求时,采取如下动作。

Ⅰ、如果Requesti <= Needi,那么转到第Ⅱ步。否则,产生出错条件,这是因为进程Pi已超过了其最大请求。

Ⅱ、如果Requesti <=Availabe,那么转到第Ⅲ步。否则,Pi必须等待,这是因为没有可用资源。

Ⅲ、假定系统可以分配给进程Pi所请求的资源,并按照如下方式修改状态:

Availabe = Availabe - Requesti ;

Allocationi = Allocationi + Requesti ;

Needi = Needi - Requesti ;

如果所产生的资源分配状态是安全的,那么交易完成且进程Pi可分配到其所需要资源。然而,如果新状态不安全,那么进程Pi必须等待Requesti 并恢复到原来资源分配状态。

简而言之,就是说当一个进程发出请求后,程序将模拟资源分配后的状况并将其交给安全性算法判断该状态是否安全,如果安全那么就将资源状态更新到分配后状态,如果不安全,则不更新。

在实现这一步时,因为我们将数据结构定义为全局变量,为了不对全局变量进行繁琐的操作,我们将另外借助临时变量存储现有的状态并对临时变量进行操作,如果新状态安全,那么再将全局变量更新到新状态,否则不更新状态。

实现完最重要的两个步骤后,剩余的初始化操作和显示操作也就水到渠成,难度不大,在此不做赘述。

(二)多线程程序的实现

因本人的g++版本更新至g++11,在g++11中,已经封装好了一类库用于专门处理多线程编程,库名叫<thread>,之前的一些旧版本的库,比如<pthread.h>库等,系统提示无法使用,所以在这里将全部使用新版的多线程库进行思路阐述。

新版本的多线程编程大概有以下操作:

1、基于<thread>库

实现初始化线程操作。

//初始化线程并指定回调函数

thread thread_name (funtion_name, funtion_parameters【如果有的话】);

//未在初始化时指定回调函数,后期需要再指定回调函数

thread_name = thread(funtion_name, funtion_parameters【如果有的话】);

或者

thread_name = thread([&]() { 定义回调函数内容 });

//等待子线程结束

thread_name.join();

2、基于<mutex>库

实现互斥锁操作。

//初始化互斥锁

mutex mutex_name;

//闭锁操作

mutex_name.lock();

//开锁操作

mutex_name.unlock();

3、基于<condition_variable>库

实现条件变量和等待、唤醒操作。

注意:条件变量必须和unique_lock锁搭配使用!

//初始化unique_lock

unique_lock<mutex> unique_lock_name(mutex_name);

//定义条件变量

condition_variable conditon_variable_name;

//条件变量的等待操作

condition_variable_name.wait(unique_lock_name, bool型变量);

condition_variable_name.wait_for();//等待时间片结束or被唤醒

condition_variable_name.wait_until();//等待时间点or被唤醒

//条件变量的唤醒操作

condition_variable_name.notify_one(); //唤醒一个等待线程,但不一定是哪一个

condition_variable_name.notify_all(); //唤醒全部等待线程

4、关于this_thread

this_thread是一个类,有4个功能函数,具体如下:

get_id();//获取当前线程的线程号

yield();//放弃线程执行,回到就绪状态

sleep_for ();//线程休眠多长时间

【eg. this_thread::sleep_for(chrono::seconds(t)),需要链接<chrono>库】

sleep_until();//线程休眠到什么时候

具体细节大家可以再查阅一下,提升认知。

实现多线程时,首先需要确定回调函数,每个线程都会调用回调函数。

回调函数主要实现以下功能:

  • 处理前先关互斥锁。
  • 利用随机数生成请求矩阵,请求矩阵不能超过该线程的Need矩阵。
  • 将请求矩阵传入资源分配算法中,判断该请求能否保证系统安全。
  • 如果可以保证系统安全,则分配资源并开锁;如果此时该线程已获得全部资源,则释放该线程的全部资源并调用另一个等待程序。
  • 如果不能保证系统安全,则将该线程堵塞,让其他线程先执行,该堵塞线程等待被唤醒。

流程图大致如下:

主函数实现:

因事先定义了进程数和资源数作为全局变量,所以在创建线程的时候采用线程数组,大小为进程数,数组中的每个线程都进行创建操作,同时每个线程创建完毕后都休眠一秒钟,防止线程之间的冲突。

创建完毕后,同样对每个线程都采取join()函数,以等待该线程的完成,完成后再打印线程资源全部分配完毕的提示语,整个程序结束。

线程与主程序的关系如下:

 

至此,程序的全部思路描述完毕,以下将贴附源码并进行运行展示。

四、源程序与相关注释

整部分代码较长,在此分段展示——

1、整体函数结构

大家先通过该图对我们要实现的程序有一个框架式的认知~~

后面我们会对每一部分进行代码贴附展示。

 

2、库及命名空间引用

#include<iostream> 
#include<thread>  //线程库
#include<chrono>  //时间库,用于休眠函数
#include<condition_variable>  //条件变量库
#include<mutex>  //互斥锁库
#include<vector>  //数组库
#include<stdlib.h>  //时间种子库,调用srand()函数,用于刷新时间

using namespace std;

3、定义全局变量 

//define global variables
int ResourcesNum = 0;  //定义资源数目,在Init()中进行赋值操作
int ProcessesNum = 0;  //定义进程数目,在Init()中进行赋值操作
vector< vector<int> > Allocation(10, vector<int> (10, 0));  //定义分配矩阵
vector< vector<int> > Max(10, vector<int> (10, 0));  //定义各进程所需的最大资源矩阵
vector< vector<int> > Need(10, vector<int> (10, 0));  //定义各进程仍需的资源矩阵
vector<int> Available(10, 0);  //定义此时空闲资源矩阵

//define count nums
int Afinish = 0;  //定义系统中已完成分配的进程数目
int wait = 0;  //定义系统中无法完成分配而被迫挂起等待的进程数目
//以上两个变量是为了确定按照系统随机数生成的请求矩阵分配资源后还能不能正常回收
//如果不能正常回收,即Afinish+wait == ProcessesNum,那么就立即停止系统运行,即使止损
//该两个变量用在回调函数Multi_Process中

//Define mutex lock
mutex mtx;  //定义互斥锁
condition_variable cv1;  //定义条件变量用于挂起和唤醒

4、功能函数详解

Ⅰ、LessOrNot

//判断一个矩阵中的每一项是否都小于另一个矩阵的每一项
bool LessOrNot(vector<int> target1, vector<int> target2, int length) {
	for(int i = 0; i < length; i++) {
		if(target1[i] > target2[i]) return false;
	}
	return true;
}

Ⅱ、PlusMatrix

//将两个矩阵相加
void PlusMatrix(vector<int> &target1, vector<int> target2, int length) {
	for(int i = 0; i < length; i++) {
		target1[i] = target1[i] + target2[i];
	}
} 

Ⅲ、SubMatrix

//将两个矩阵相减
void SubMatrix(vector<int> &target1, vector<int> target2, int length) {
	for(int i = 0; i < length; i++) {
		target1[i] = target1[i] - target2[i];
	}
}

Ⅳ、Release

//将进程占据的资源全部释放,用于该进程已经完成的情况
void Release(int id) {
	PlusMatrix(Available, Allocation[id], ResourcesNum);
	return ;
} 

5、初始化函数详解

/*初始化函数*/
void Init() {  
	cout << "********************************************" << endl;
	cout << "Written  by  wyr" << endl;
	cout << "Please respect originality." << endl;
	cout << "********************************************" << endl;
	cout << endl;
	cout << "Please enter the number of resources(1~10):" << endl;  //输入资源总数
	cin >> ResourcesNum;
	cout << "Please enter the number of processes(1~10):" << endl;  //输入进程总数
	cin >> ProcessesNum;
	cout << "Please enter the maximum number of various resources required by each process in turn:" << endl;  //输入各个进程所需要的各种资源的最大数目
	for(int i = 0; i < ProcessesNum; i++) {
	    for(int j = 0; j < ResourcesNum; j++) {
			cin >> Max[i][j];//Input the maximum demand matrix
	}
  }
	cout << "Please enter the number of various resources that have been allocated to each process in turn" << endl;//输入各个进程已被分配的各种资源的数目
        for(int i = 0; i < ProcessesNum; i++) {
            for(int j = 0; j < ResourcesNum; j++) {
                        cin >> Allocation[i][j];//Input distribution matrix
        }
  }
	cout << "Please enter the available quantity of various resources" << endl;  //输入现在空闲可使用的资源数目
	for(int i = 0; i < ResourcesNum; i++) {
		cin >> Available[i];//input the available matrix
  }
        for(int i = 0; i < ProcessesNum; i++) {//根据输入的Max矩阵和Allocation矩阵计算Need矩阵
            for(int j = 0; j < ResourcesNum; j++) {
                        Need[i][j] = Max[i][j] - Allocation[i][j];
        }
  }
}

6、显示函数详解

/*显示函数*/
void Show() {
	cout << "---------------------------------------------------------------------------------" << endl;
	cout << "Current state is as follows:" << endl;
	//打印分配矩阵
	cout << "____Allocation____";
	for(int i = 0; i < ProcessesNum; i++) {
		cout << endl;
		cout<<"P"<<i<<"  ";
	    for(int j = 0; j < ResourcesNum; j++) {
		    cout << Allocation[i][j] << "  " ;
	}
}
	//打印Max矩阵
	cout << endl;
        cout << "____Max____";
        for(int i = 0; i < ProcessesNum; i++) {
                cout << endl;
                cout<<"P"<<i<<"  ";
            for(int j = 0; j < ResourcesNum; j++) {
                    cout << Max[i][j] << "  " ;
        }
}
        cout << endl;
	//打印Need矩阵
 	cout << "____Need____";
        for(int i = 0; i < ProcessesNum; i++) {
                cout << endl;
                cout<<"P"<<i<<"  ";
            for(int j = 0; j < ResourcesNum; j++) {
                    cout << Need[i][j] << "  " ;
        }
}
	cout << endl;
	//打印Available矩阵
	cout << "____Available____" << endl;
	for(int i = 0; i < ResourcesNum; i++) {
		cout << Available[i] << "  ";
	}
	cout << endl << "---------------------------------------------------------------------------------" << endl;
	cout << endl;
}

7、安全性算法详解

bool SecurityOrNot(vector<vector<int>> Allocation1, vector<vector<int>> Max1, vector<vector<int>> Need1, vector<int> Available1, int ResourcesNum1, int ProcessesNum1) {
	vector<int> Work = Available1;  //定义Work矩阵,将Availabe矩阵的值赋给他
	vector<int> Finish1(10,0);  //定义Finish矩阵,标记某进程是否已完成
	vector<int> Sequence;  //定义Sequence矩阵,标记进程完成的顺序并用于打印显示
	while(1) {
	int i = 0;
	for(i = 0; i < ProcessesNum1; i++) {
		if(Finish1[i] == 0 && LessOrNot(Need1[i], Work, ResourcesNum1)) {//根据之前思路分析中有关安全性算法的描述,这是在判断有没有一个进程未完成且需求量Need小于空闲量Availabe
			PlusMatrix(Work, Allocation1[i], ResourcesNum1);//如果有,则说明该进程可以完成,并将之前分配给该进程的资源回收
			Finish1[i] = 1;//标记该进程已完成
			Sequence.push_back(i);//将进程放入Sequence矩阵中
			break;//跳出循环,进行下一次循环
		    }
	}
	if(i == ProcessesNum1) {//没有一个进程未完成或需求量Need小于空闲量Availabe,则判断是不是所有进程都执行完毕了。
		for(int j = 0; j < ProcessesNum1; j++) {
			if(Finish1[j] == 0) return false;//如果不是,则说明不能安全回收序列,返回false
		}
		//如果是,就打印出进程的执行顺序,并返回true。
		cout << "该系统状态是安全的,可以找到一个安全序列满足条件,该安全序列为:" << endl;
		for(int i = 0; i < Sequence.size(); i++) {
			cout << "P" << Sequence[i] << "  " ;
		}
		cout << endl;
		return true;
	}
	}
	return true;
}

8、资源分配算法详解(内部会链接安全性算法)

/*资源分配算法*/
bool ResourceRequest(vector<int> request, int processnum) {
	//定义临时存储矩阵
	vector< vector<int> > tempAllocation = Allocation;
	vector< vector<int> > tempMax = Max;
	vector< vector<int> > tempNeed = Need;
	vector<int> tempAvailable = Available;
	if(LessOrNot(request, tempNeed[processnum], ResourcesNum)) {//如果请求矩阵比进程实际的需求矩阵还要大,那就不予通过,直接跳转到报错三;如果满足条件,进入下一步判断
		if(LessOrNot(request, tempAvailable, ResourcesNum))  {//如果请求矩阵比现有的空闲资源矩阵还要大,那就不予通过,直接跳转到报错二;如果满足条件,进入下一步判断
		    //上述两个判断都通过,则虚拟分配一下资源
			SubMatrix(tempAvailable,request,ResourcesNum);
		    PlusMatrix(tempAllocation[processnum], request,ResourcesNum);
		    SubMatrix(tempNeed[processnum], request,ResourcesNum);
		    if(SecurityOrNot(tempAllocation, tempMax, tempNeed, tempAvailable, ResourcesNum, ProcessesNum)) {//判断分配后的资源是否满足安全性算法,如果不满足,就直接跳转到报错一
			//如果满足,那么就将实际的资源矩阵分配完毕,并提示分配成功
			SubMatrix(Available,request,ResourcesNum);
			PlusMatrix(Allocation[processnum], request,ResourcesNum);
			SubMatrix(Need[processnum], request,ResourcesNum);
			cout << "P" << processnum << "分配状态安全,已完成分配,新的分配状态如下: " <<  endl;
			cout << endl;
			//Show();
			return true;
		    }
		    else {
		    	cout << "P" << processnum << "分配状态不安全,不进行资源分配!" << endl;//报错一
				cout << endl;
				return false;
		    	}
		   }
		else {  
			cout << "P" << processnum << "没有可用资源,请求失败!" << endl;//报错二
			cout << endl;
			return false;
			}
	    }
	    else {
	    	cout << "P" << processnum << "进程已超过其最大请求!" << endl;//报错三
			cout << endl;
			return false;
	    	}
	}	

9、多线程回调函数

/*多线程函数*/
void Multi_Process(int index) {	
	unique_lock<mutex> lock(mtx);//定义unique_lock锁并自动加锁
	int id = index;//进程的编号
	bool NotFinishFlag = 1;//未完成的标志
	while(NotFinishFlag) {
		vector<int> request(ResourcesNum, 0);
		cout << "P" << id << "当前请求资源矩阵为:" ;
		for(int i = 0; i < ResourcesNum; i++) {//随即生成请求矩阵
			if(Need[id][i] == 0) {
				request[i] = 0;  //如果需求矩阵对于该资源的需求量本身为0,那么请求矩阵对该资源的请求自动设为0
				cout << "  " << request[i];
			}
			else {
				do {
				request[i] = rand() % (Need[id][i] + 1);//否则,生成一个不为0的请求
				}while(request[i] == 0);
				cout << "  " << request[i];
			}
		}
		cout << endl;
		bool cur = ResourceRequest(request, id);  //将请求矩阵调入资源分配算法判断是否可以安全运行
		if(cur) {//如果可以安全运行,那么判断进程是否执行完毕
			int i = 0;
			for(i = 0; i < ResourcesNum; i++) {
				if(Need[id][i] != 0) break;
			}
			if(i == ResourcesNum) {
				NotFinishFlag = 0;//标记自己已经完成,可以退出循环,完成线程的任务
				cout << "P" << id << "进程资源分配完毕" << endl;
				Release(id);
				cout << "P" << id << "进程执行完毕" << endl;
				cout << endl;
				Show();
				Afinish++;
				wait--;
				cv1.notify_one();//如果执行完毕,那么就打印成功信息,并且唤醒在等待的进程
				return;
				}
			else {
				//该步处理条件非常重要!!
                cv1.notify_one();//如果进程没有执行完毕,那么释放锁,唤醒一个等待进程
				this_thread::yield();//同时自身完成中止执行,返回到就绪状态,等待下一次执行
			}//写多线程程序的时候,一定需要保证使用notify的线程在使用wait线程之后执行。
			}
		else {
			 wait++;
			 //cout << "wait = " << wait << endl;
			 if(wait + Afinish == ProcessesNum) {//如果等待进程和完成进程数相加刚好为ProcessesNum,说明此时系统陷入死循环,无论作何请求都会发生死锁,此时应该立即终止!
			 	cout << "银行家分配算法达到极限,再分配下去就陷入将陷入死锁状态,就此停止!请手动CTRL+C停止!" << endl;
			 	return;
			  }
			cv1.wait(lock);	//如果进程的请求不满足安全性算法,那么就自动挂起堵塞,等待其他进程唤醒。
		}	
	}
	return ;
}

10Main函数

int main() {
	srand(time(NULL));//设置时间种子,随时间不同而刷新请求矩阵的值
	Init();//初始化函数,设计输入操作
	Show();//显示初始化信息
	thread thread_pool[ProcessesNum]; //定义ProcessesNum个线程
	for(int i = 0; i < ProcessesNum; i++) {
		thread_pool[i] = thread(Multi_Process, i);//创建线程
		this_thread::sleep_for(chrono::seconds(1));//执行完后睡眠1s
}
	for(int i = 0; i < ProcessesNum; i++) {
		thread_pool[i].join();//让主线程等待所有线程的完成
	}
	
	cout << endl << "***********************************************" << endl;
	cout << "至此,所有线程执行完毕!" << endl;//线程执行完毕
	return 0;
}

五、总结

上述代码我都已在代码行中添加了详细注释,将所有代码贴附到一起即可正常运行,在此陈列出来主要是为了给大家提供一个样本作为练习,同时也是为了让老师在审阅自己作业的时候能运行一下源码看看对不对哈哈哈哈~

大家注意在运行的时候,只能在ubuntu中使用g++编译器运行,而不能在vs上运行,同时运行的指令如下:

g++ ***.cpp -o *** -lpthread

一定要链接最后的lpthread库,否则的话是编译不成功的!

最终实现效果如下——

运行程序后,初值如下:(这是我手动设置的初值,程序初始运行时会提示你输入初值)

最终运行结果如下:

 

 

笔者感觉这个程序真的很有难度,希望这篇博客能对uu们有所帮助!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值