(C++/进程(线程)调度算法)显式单线程模拟抢占式HRRN和FCFS——两种最极端的调度算法

什么是调度?调度有什么作用?

        调度,顾名思义,就是分配。这就好比是士兵和战场指挥官的关系,当战场上的某一侧士兵满足某个条件时,就会被调到另一边。而这里的线程就相当于士兵,而操作系统和CPU就相当于战场上的指挥官,当满足这些算法里面的条件时,就会被调度。

        调度可以最大化利用CPU的性能,尽量的减少CPU的闲置率,从而达到提升性能的效果。

调度算法介绍

        调度算法有FCFS,RR,SJF(抢占式和非抢占式),EDF,HRRN(抢占式和非抢占式)等,这个其他的博主已经介绍的非常详细了,我们专注于代码实现。

        参考链接:常用的调度算法(包含实例)|操作系统-CSDN博客

HRRN算法简介

        HRRN算法(最高响应比算法)分为抢占式和非抢占式两种,每次调度时都会选择响应比最高的进程(现成)进入到执行队列中,当HRRN分别为抢占式或非抢占式时,分别有两种计算方式:

        抢占式HRRN:响应比=\frac{W}{L}+1(W为等待时间,L为该进程剩余时间)

        非抢占式HRRN:响应比=\frac{W}{S}+1(W为等待时间,S为预计执行时间)

        请注意,W+S的时间在数值上等于周转时间,但是我们无法在进程执行的时候提前得知具体的周转时长,需要灵活使用。

FCFS算法简介

        FCFS(先来先服务)算法,是让进程(线程)按照顺序执行的一种方法,当前一个进程调度结束或报错之前,一般不会进行后一个任务。

HRRN和FCFS的利弊和使用情景

HRRN算法:结合了FCFS和SJF的优点,对长短作业都较公平,但是会进行频繁的线程切换,导致效率较低。需要同时执行长短作业的时候会被使用,例:Windows系统(非抢占式HRRN)

FCFS算法:平均周转时间较长,但是线程切换最少,在性能要求较高的时候会被使用。

代码实现

        我们这里在FCFS的部分中使用了队列作为容器,而在HRRN算法中使用了vector作为容器,主要是考虑到精简容器的需求,方法调用更加清晰。以下是代码实现:

#include<iostream>
#include<vector>
#include<queue>
using namespace std;
#define NotFound -1
#define None -1
//我们假设进程会一直执行,在执行完毕之前不会被强制退出或停止
//且会按照进程的下标顺序逐个进行
typedef struct Process
{
	int id;//id号
	int arrivalTime;//到达时间
	int burstTime;//执行时间
	int leftTime;//剩余时间
	int startTime;//开始时间
	int finishTime;//完成时间
	int turnaroundTime;//周转时间
	//初始化↓
	Process(int id,int arrivalTime,int burstTime,int leftTime) :id(id), arrivalTime(arrivalTime), burstTime(burstTime),leftTime(leftTime),startTime(None), finishTime(None), turnaroundTime(None) {};
}Process;
class FCFS_Analysis
{
public:
	void caculateFCFSTimes();//求FCFS的开始时间、完成时间、周转时间
	void printProcesses();
private:
	int nowtime = 0;
	int totalTurnaroundTime = 0;
	float averageTurnaroundTime = 0;
	float totalWeighedTurnaroundTime = 0;
	float averageWeighedTurnaroundTime = 0;
	vector<Process> processes =
	{
		{1,0,3,3},
		{2,2,6,6},
		{3,4,4,4},
		{4,6,5,5},
		{5,8,2,2},
	};
	queue<Process> wait_Queue;
	queue<Process> bursting_Queue;
};
class HRRN_Analysis//抢占式HRRN
{
public:
	void caculateHRRNTimes();
	float caculateResponseRatio(const Process& process);
	void printProcesses();
	int findIndexByID(int targetProcessID);//结构体里的id是小写的
private:
	int nowtime = 0;
	int totalTurnaroundTime = 0;
	float averageTurnaroundTime = 0;
	float totalWeighedTurnaroundTime = 0;
	float averageWeighedTurnaroundTime = 0;
	vector<Process> processes =
	{
		{1,0,3,3},
		{2,2,6,6},
		{3,4,4,4},
		{4,6,5,5},
		{5,8,2,2},
	};
	queue<Process> wait_Queue;
	queue<Process> bursting_Queue;
};

//FCFS算法
void FCFS_Analysis::caculateFCFSTimes()
{
	bool allDone = false;
	int burstingProcessIndex = 0;//用于while循环,记录的是正在进行的进程,对应的是processes里面的
	int arrivingProcessIndex = 0;//指示即将到达的进程在vector里的下标,对应的也是processes里面的
	int completeProcessesNum = 0;//记录已经完成的进程,用于后续完成的判断
	while (!allDone)
	{
		//模拟进入等待队列的过程
		if (arrivingProcessIndex < processes.size())
		{
			if (nowtime == processes[arrivingProcessIndex].arrivalTime)
			{
				if (processes[arrivingProcessIndex].leftTime > 0)//此处加入了leftTime的判定,防止新进程为空
				{
					wait_Queue.push(processes[arrivingProcessIndex]);//如果nowtime等于到达时间,若burstTime大于0,那么就会加入等待队列,然后等待执行
					arrivingProcessIndex++;
				}
				else
				{
					arrivingProcessIndex++;
					burstingProcessIndex++;
					completeProcessesNum++;
				}
			}
		}		

		//模拟进入执行队列的过程
		//这里没有信号量,所以等待队列最多只有一个元素,应该加上信号量,但题目没有给出
		if (!wait_Queue.empty())//只需要队列大小判空,因为前面已经对wait_Queue里面的进程执行时间判空过了
		{
			bursting_Queue.push(wait_Queue.front());
			wait_Queue.pop();
		}
		//模拟在执行队列中执行的过程
		if (!bursting_Queue.empty())
		{
			if (bursting_Queue.front().burstTime == bursting_Queue.front().leftTime)
			{
				bursting_Queue.front().startTime = nowtime;//如果是新进程,则赋开始时间的值
			}
			bursting_Queue.front().leftTime--;
			nowtime++;
			if (bursting_Queue.front().leftTime == 0)
			{
				//开始结束时间
				bursting_Queue.front().finishTime = nowtime;//这个是执行队列出队之前
				bursting_Queue.front().turnaroundTime = bursting_Queue.front().finishTime - bursting_Queue.front().arrivalTime;
				processes[burstingProcessIndex] = bursting_Queue.front();
				bursting_Queue.pop(); 
				burstingProcessIndex++;//因为这里是FCFS,所以可以直接++
				completeProcessesNum++;
			}
		}
		else nowtime++;
		//判断有没有全部完成
		if (completeProcessesNum == processes.size())
		{
			allDone = true;
			for (const Process& tempProcess : processes)
			{
				totalTurnaroundTime = totalTurnaroundTime + tempProcess.turnaroundTime;
				totalWeighedTurnaroundTime = totalWeighedTurnaroundTime + (tempProcess.turnaroundTime / tempProcess.burstTime);
			}
			averageTurnaroundTime = static_cast<float>(totalTurnaroundTime) / processes.size();
			averageWeighedTurnaroundTime = static_cast<float>(totalWeighedTurnaroundTime) / processes.size();
		}
	}
}
void FCFS_Analysis::printProcesses()
{
	cout << "FCFS算法数据如下:" << endl;
	for (const Process& tempProcess : processes)
	{
		cout << "进程id: " << tempProcess.id << "开始时间: " << tempProcess.startTime << "完成时间: " << tempProcess.finishTime << "周转时间: " << tempProcess.turnaroundTime << endl;
	}
	cout << "平均周转时间: " << averageTurnaroundTime << "平均带权周转时间: " << averageWeighedTurnaroundTime << endl;
}


//HRRN算法
float HRRN_Analysis::caculateResponseRatio(const Process& process)
{
	//这个响应比在数值上等于单个进程的带权周转时长,但是没有办法提前得知实际周转时长,所以采用等待时间/剩余时间+1来计算
	if (process.leftTime == 0)
	{
		return 0.0f;
	}
	else
	{
		if (process.startTime==None)
		{
			return static_cast<float>(nowtime - process.arrivalTime) / process.leftTime + 1;
		}
		else
		{
			return static_cast<float>(nowtime-process.arrivalTime-(process.burstTime-process.leftTime))/process.leftTime+1;
		}

	}
}
int HRRN_Analysis::findIndexByID(int targetProcessID)//这个方法是在processes里面找index
{
	if (targetProcessID != NotFound)
	{
		for (int i = 0; i < processes.size(); i++)
		{
			if (processes[i].id == targetProcessID)
			{
				return i;
			}
		}
		return NotFound;//若没找到对应id编号的进程,则返回NotFound,作为未找到的判别值
	}
	else return NotFound;//如果没有给ID赋值,则也是输出NotFound
}
void HRRN_Analysis::caculateHRRNTimes()
{
	bool allDone = false;
	int burstingProcessIndex = NotFound;//用于while循环,记录的是正在进行的进程,对应的是processes里面的,初始化,因为0号进程是最先到的,所以初始化为0
	int arrivingProcessIndex = 0;//指示即将到达的进程在vector里的下标,对应的也是processes里面的
	int completeProcessesNum = 0;//记录已经完成进程的数量,用于后续判定完成的判断
	vector<Process*> wait_Queue;
	vector<Process*> bursting_Queue;
	while (!allDone)
	{
		int selectedProcessIndex = NotFound;
		int selectedProcessID = NotFound;
		float maxProcessRatio = None;//需要判空,当执行队列为空的时候需要进行额外操作
		//模拟进程到达时进入等待队列的过程
		if (arrivingProcessIndex < processes.size())//一定要先判定数组有没有越界,不然会报越界错误,因为没有processes[arrivingProcessIndex]这种东西
		{
			if (nowtime == processes[arrivingProcessIndex].arrivalTime)
			{
				if (processes[arrivingProcessIndex].leftTime > 0)//此处加入了leftTime的判定,防止新进程为空
				{
					wait_Queue.push_back(&processes[arrivingProcessIndex]);//如果nowtime等于到达时间,且burstTime大于0,那么就会加入等待队列,然后等待执行
					arrivingProcessIndex++;
				}
				else
				{
					//这里并没有涉及到当前进行的任务的下标
					arrivingProcessIndex++;
					completeProcessesNum++;
				}
			}
		}
		

		//以下是最高响应比的计算,需要有两块,一块是执行队列的,一块是等待队列的
		//执行队列↓
		for(const Process* tempProcess:bursting_Queue)//把所有进程的响应比都计算一遍,然后存储最大值
		{
			float tempResponseRatioValue = caculateResponseRatio(*tempProcess);//存储一个临时的响应比的值,使代码更直观,也减少了重复计算
			if (tempResponseRatioValue > maxProcessRatio)
			{			
				maxProcessRatio = tempResponseRatioValue; 
				selectedProcessID = tempProcess->id;//得到该进程的下标
			}
		}
		//等待队列↓
		for (const Process* tempProcess:wait_Queue)
		{
			float tempResponseRatioValue = caculateResponseRatio(*tempProcess);//存储一个临时的响应比的值,使代码更直观,也减少了重复计算
			if (tempResponseRatioValue > maxProcessRatio)
			{
				maxProcessRatio = tempResponseRatioValue;
 				selectedProcessID = tempProcess->id;//得到该进程的下标
			}
		}
		//最后在两个队列中挑选出来的进程下标,先对selectedProcessIndex判空,否则就输出
		if (selectedProcessID == NotFound)//但是这里有一个问题,就是会导致忙等,且可能会造成死循环
		{
			nowtime++;
			continue;
		}
		selectedProcessIndex = findIndexByID(selectedProcessID);//可以直接用,因为当ID为未找到的时候,也能正常调用该方法
		
		
		//把原先的执行队列放回等待队列,如果进程相同则不用放回,并且存储leftTime用于使用
		//因为操作系统在同一时刻只能执行一个任务,所以可以直接把执行队列中的第一个元素出队(这里是以vector为容器)
		//要判空,因为很有可能上一个进程刚刚结束,已经从执行队列中移出
		bool isSelectedProcessAlreadyInBurstingQueue = false;//判断是不是相同进程,避免重复调用
		if (!bursting_Queue.empty())
		{
			for (int i=0;i<bursting_Queue.size();i++)
			{
				if (bursting_Queue[i]->id == selectedProcessID)
				{
					isSelectedProcessAlreadyInBurstingQueue = true;
					break;
				}
			}
			if (!isSelectedProcessAlreadyInBurstingQueue)
			{
				vector<Process*>::iterator it=find(bursting_Queue.begin(),bursting_Queue.end(),&processes[selectedProcessIndex]);
				if (it != bursting_Queue.end())
				{
					wait_Queue.push_back(*it);
					bursting_Queue.erase(it);
				}				
			}
		}
		

		//模拟进入执行队列的过程
		if (!wait_Queue.empty())//只需要队列大小判空,因为前面已经对wait_Queue里面的进程执行时间判空过了
		{
			if (!isSelectedProcessAlreadyInBurstingQueue)
			{
				bursting_Queue.push_back(&processes[selectedProcessIndex]);
				vector<Process*>::iterator toErase = find(wait_Queue.begin(), wait_Queue.end(), &processes[selectedProcessIndex]);
				if (toErase != wait_Queue.end())
				{
					wait_Queue.erase(toErase);
				}
			}
		}
		else
		{
			if (selectedProcessIndex != NotFound&&!isSelectedProcessAlreadyInBurstingQueue)
			{
				bursting_Queue.push_back(&processes[selectedProcessIndex]);
			}
		}

		//模拟在执行队列中执行的过程	
		if (!bursting_Queue.empty())
		{
			burstingProcessIndex = selectedProcessIndex;
			if (processes[burstingProcessIndex].burstTime == processes[burstingProcessIndex].leftTime)
			{
				processes[burstingProcessIndex].startTime = nowtime;//如果是新进程,则赋开始时间的值
			}
			processes[burstingProcessIndex].leftTime--;
			nowtime++;
			if (processes[burstingProcessIndex].leftTime == 0)
			{
				//开始结束时间
				processes[burstingProcessIndex].finishTime = nowtime;//这个是执行队列出队之前的进程
				processes[burstingProcessIndex].turnaroundTime = processes[burstingProcessIndex].finishTime - processes[burstingProcessIndex].arrivalTime;
				vector<Process*>::iterator toErase = find(bursting_Queue.begin(), bursting_Queue.end(), &processes[burstingProcessIndex]);//找到执行队列中的迭代器,然后使其出队
				if (toErase != bursting_Queue.end())
				{
					bursting_Queue.erase(toErase);
				}
				completeProcessesNum++;
			}
		}
		else nowtime++;
		//判断有没有全部完成
		if (completeProcessesNum == processes.size())
		{
			allDone = true;
			for (const Process& tempProcess : processes)
			{
				totalTurnaroundTime = totalTurnaroundTime + tempProcess.turnaroundTime;
				totalWeighedTurnaroundTime = totalWeighedTurnaroundTime + (tempProcess.turnaroundTime / tempProcess.burstTime);
			}
			//我们默认进程数是正整数
			averageTurnaroundTime = static_cast<float>(totalTurnaroundTime) / processes.size();
			averageWeighedTurnaroundTime = static_cast<float>(totalWeighedTurnaroundTime) / processes.size();
		}
	}
}
void HRRN_Analysis::printProcesses()
{
	cout << "HRRN算法的数据如下:" << endl;
	for (const Process& tempProcess : processes)
	{
		cout << "进程id: " << tempProcess.id <<"到达时间: "<<tempProcess.arrivalTime << "开始时间: " << tempProcess.startTime << "完成时间: " << tempProcess.finishTime << "周转时间: " << tempProcess.turnaroundTime << endl;
	}
	cout << "平均周转时间: " << averageTurnaroundTime << "平均带权周转时间: " << averageWeighedTurnaroundTime << endl;
}
int main()
{
	FCFS_Analysis FCFS;
	FCFS.caculateFCFSTimes();
	FCFS.printProcesses();

	HRRN_Analysis HRRN;
	HRRN.caculateHRRNTimes();
	HRRN.printProcesses();

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Sunrise丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值