消息传递接口
来源: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”一直在等待发送命令;进入“死锁”状态。
(原题中此处有错误,已更正)
提示
解题思路
初始时,所有进程处于就绪状态。若进程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;
}