消息传递接口

来源:CCF

标签:

参考资料:

相似题目:

背景

消息传递接口(MPI)是一个并行计算的应用程序接口,常用于超级计算机、计算机集群等环境下的程序设计。

题目

老师给了 T 份 MPI 的样例代码,每份代码都实现了 n 个进程通信。这些进程标号从 0 到 n − 1,每个进程会顺序执行自己的收发指令,如:“S x”,“R x”。“S x”表示向x 号进程发送数据,“R x”表示从 x 号进程接收数据。每一对收发命令必须匹配执行才能生效,否则会“死锁”。
举个例子,x 号进程先执行发送命令“S y”,y 号进程必. 须. 执行接送命令“R x”,这一对命令才执行成功。否则 x 号进程会一直等待 y 号进程执行对应的接收命令。反之,若 y 号进程先执行接收命令“R x”,则会一直等待 x 号进程执行发送命令“S y”,若 x号进程一直未执行发送命令“S y”,则 y 号进程会一直等待 x 号进程执行对应的发送命令。上述这样发送接收命令不匹配的情况都会造成整个程序出现“死锁”。另外,x
号进程不会执行“S x”或“R x”,即不会从自己的进程收发消息。.现在老师请你判断每份样例代码是否会出现“死锁”的情况。每个进程的指令最少有 1 条,最多有 8 条,这些指令按顺序执行,即第一条执行完毕,才能执行第二条,依次到最后一条。

输入

从标准输入读入数据。
输入第一行两个正整数 T, n,表示有 T 份样例代码,实现了 n 个进程通信。
接下来有 T × n 行,每行有若干个(1 − 8 个)字符串,相邻之间有一个空格隔开,表示相应进程的收发指令。不存在非法指令。对于第 2 + i, 0 ≤ i ≤ (T × n − 1) 行,表示第 i ÷ n(商)份代码的 i KQ/ n(余数)号进程的收发指令。
(比如,“S1”表示向 1 号进程发送消息,“R1”表示从 1 号进程接收消息。细节请
参考样例。)

输出

输出到标准输出。
输出共 T 行,每行一个数字,表示对应样例代码是否出现“死锁”的情况。1 表示死锁,0 表示不死锁。

输入样例1

3 2
R1 S1
S0 R0
R1 S1
R0 S0
R1 R1 R1 R1 S1 S1 S1 S1
S0 S0 S0 S0 R0 R0 R0 R0

输出样例1

0
1
0

样例解释1

第 1 份代码中,(1)0 号进程执行的“R1”和 1 号进程执行的“S0”成功执行;(2)0 号进程执行的“S1”和 1 号进程执行的“R0”成功执行,所以未发生“死锁”,程序顺利运行。
第 2 份代码中,(1)0 号进程执行的“R1”和 1 号进程执行的“R0”一直在等待发送命令,进入“死锁”状态。
(原题中此处有错误,已更正)

输入样例2

2 3
R1 S1
R2 S0 R0 S2
S1 R1
R1
R2 S0 R0
S1 R1

输出样例2

0
1

样例解释2

第 1 份代码中,(1)2 号进程执行的“S1”和 1 号进程执行的“R2”成功执行;(2)0 号进程执行的“R1”和 1 号进程执行的“S0”成功执行;(3)0 号进程执行的“S1”和 1 号进程执行的“R0”成功执行;(4)1 号进程执行的“S2”和 2 号进程执行的“R1”成功执行;所以未发生“死锁”,程序顺利运行。
第 2 份代码中,(1)2 号进程执行的“S1”和 1 号进程执行的“R2”成功执行;(2)
0 号进程执行的“R1”和 1 号进程执行的“S0”成功执行;(3)1 号进程执行的“R0”
和 2 号进程执行的“R1”一直在等待发送命令;进入“死锁”状态。
(原题中此处有错误,已更正)

提示

CCF计算机软件能力认证试题练习:201903-4 消息传递接口

解题思路

初始时,所有进程处于就绪状态。若进程A发送数据到进程B,那么B必须接收来自A的消息,否则A将处于等待状态。若B没有接收来自A的消息,反而去给进程C发送数据了,那么同样C必须接收来自B的消息,否则B也处于等待状态…直到C接收了B的数据,B才处于就绪状态,才能处理下一个消息。A也一样。所以简单地模拟这个递归过程,具体的请参考代码。

参考代码

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<string>
#include<sstream>
#include<queue>
using namespace std;
const int MAXN=10005;

struct Mes{ //消息结构体
	char flag; //R:收,S:发
	int target; //目标进程
};

struct Pro{ //进程结构体
	queue<Mes> task; //消息队列
};

Pro pro[MAXN]; //进程数组,pro[i]代表进程i
int wait[MAXN]; //进程等待标志,1为等待,0为就绪

int exe(int no); //执行进程no
int receiveM(int from, int to); //进程from从进程to那里接收消息
int sendM(int from, int to); //进程from向进程to那里发送消息

int exe(int no){
	if(wait[no]==1) return -1; //如果进程处于等待状态,那么发生死锁
	if(pro[no].task.empty()) return 0; //如果进程的消息队列为空,那么表示该进程成功结束
	Mes cur=pro[no].task.front(); //取第一个消息
	if(cur.flag=='R'){
		wait[no]=1;
		if(receiveM(no, cur.target)==-1) return -1;
		
		//如果该消息成功执行 
		pro[no].task.pop();
		wait[no]=0;
		exe(no); //递归处理该进程的下一个消息
	}
	else if(cur.flag=='S'){
		wait[no]=1;
		if(sendM(no, cur.target)==-1) return -1;
		
		//如果该消息成功执行 
		pro[no].task.pop();
		wait[no]=0;
		exe(no); //递归处理该进程的下一个消息
	}
	return 0;
}

int receiveM(int from, int to){ 
	if(wait[to]==1) return -1; //判断目标进程的状态
	if(pro[to].task.empty()) return -1; //判断目标进程的消息队列是否为空
	Mes cur=pro[to].task.front(); //获取目标进程的消息队列中的第一个消息,判断能否处理来自from的消息 
	if(cur.flag=='R'){ //不能处理 
		wait[to]=1;
		if(receiveM(to, cur.target)==-1)  return -1;//递归处理目标进程的下个消息
		 
		wait[to]=0;
		pro[to].task.pop();
		if(receiveM(from, to)==0) return 0; //递归判断之前的消息能否得到处理 
		return -1;
	}
	else if(cur.flag=='S'){ //因为是'S',或许可以处理当前消息 
		if(cur.target==from){ //确实可以处理当前消息 
			pro[to].task.pop();
			return 0;
		}
		
		//不能处理当前消息 
		wait[to]=1;
		if(sendM(to, cur.target)==-1) return -1; //递归处理目标进程的下个消息 
		
		wait[to]=0;
		pro[to].task.pop();
		if(receiveM(from, to)==0) return 0; //递归判断之前的消息能否得到处理
		return -1; 
	}
	return -1;
}

int sendM(int from, int to){ 
	if(wait[to]==1) return -1;
	if(pro[to].task.empty()) return -1;
	Mes cur=pro[to].task.front();
	if(cur.flag=='R'){
		if(cur.target==from){
			pro[to].task.pop();
			return 0;
		}
		
		wait[to]=1;
		if(receiveM(to, cur.target)==-1) return -1;
			
		wait[to]=0;
		pro[to].task.pop();
		if(sendM(from, to)==0) return 0;
		return -1;
	}
	else if(cur.flag=='S'){
		wait[to]=1;
		if(sendM(to, cur.target)==-1) return -1;
		
		wait[to]=0;
		pro[to].task.pop();
		if(sendM(from, to)==0) return 0;
		return -1;
	}
	return -1;
}

int T,n;

int main(){
	scanf("%d%d",&T,&n);
	getchar();
	for(int i=0;i<T;i++){
		
		memset(wait,0,sizeof(wait));
		
		for(int j=0;j<n;j++){ //初始化每个进程的消息队列
			while(!pro[j].task.empty()) pro[j].task.pop();
		}
		
		for(int j=0;j<n;j++){ //存储进程的消息队列
			string line;
			getline(cin,line);
			istringstream ss(line);
			string tmp;
			while(ss>>tmp){
				Mes mes;
				mes.flag=tmp[0];
				mes.target=atoi(tmp.c_str()+1);
				pro[j].task.push(mes);
			}
		}
		
		int flag=0;
		for(int j=0;j<n;j++){
			if(!pro[j].task.empty()){ //如果某个进程的消息队列非空,就执行该进程
				if(exe(j)==-1){
					flag=1;
					break;
				}
			}
		}
		
		printf("%d\n",flag);
		
	}
	return 0;
}

相关文章: