名称:利用无名管道实现简单并行计算
说明:本程序利用无名管道进行进程间通信,达到多进程同时计算的目的。
基本算法是:首先在主进程里创建了一定数量的子进程,然后主进程通过无名管道向子进程传递要计算的数据,子进程计算后,通过无名管道将计算的结果(此处是求和)返回给主进程,主进程进行汇总。
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
#include <malloc.h>
#include <time.h>
#define READ_END 0
#define WRITE_END 1
#define PRO_NUM 4 //进程数量
#define DATA_NUM 100 //数据数量
#define NUM (DATA_NUM / DATA_NUM) //每个进程所要处理的数据个数;
//#define NUM (PRO_NUM)/(DATA_NUM) //运行结果有错误
#define NUM 100 / 4
typedef struct
{
int main_to_sub_fd[2]; //信息流从主进程流向子进程,管道读写描述符(即主进程向子进程传递数据)
int sub_to_main_fd[2]; //信息流从子进程流向主进程,管道读写描述符(即子进程将计算的结果返回给主进程)
}FD_table;
FD_table fdtable[PRO_NUM];
//创造模拟的随机数数据,此处的种子是进程序号
void create_rand(int ** data,int ran)
{
int i = 0;
*data = (int *)malloc(sizeof(int) * NUM); //申请数据空间
srand(ran);
for(;i<NUM;++i)
{
(*data)[i] = rand() % 100 + 1; //产生100以内的随机数
}
}
//产生管道表,用于主进程查询用
int create_pipe_table()
{
int i = 0;
for(;i<PRO_NUM;++i)
{
if(-1 == pipe(fdtable[i].main_to_sub_fd)) //创造管道
{
printf("pipe failed.\n");
return 1;
}
if(-1 == pipe(fdtable[i].sub_to_main_fd)) //创造管道
{
printf("pipe failed.\n");
return 1;
}
}
return 0;
}
//每个子进程求和
int cal(int data[])
{
int i = 0;
int sum = 0;
for(;i<NUM;++i)
{
sum = sum + data[i];
}
return sum;
}
//子进程的所有操作
void child_pro(int data[],int flag)
{
int sum = 0;
int i = 0;
read((fdtable[flag]).main_to_sub_fd[READ_END],data,sizeof(int)*NUM); //子进程从主进程的管道中读取数据
sum = cal(data); //每个子进程计算求和
printf("(%d):the sum is %d\n",getpid(),sum);
write(fdtable[flag].sub_to_main_fd[WRITE_END],&sum,sizeof(int)); //子进程计算结果写入到主进程
}
int main()
{
int temp = 0;
pid_t pid;
int i = 0;
int flag = 0; //用来识别进程的编号
int sums[PRO_NUM];
int total_sum = 0;
int *_data = NULL; //用于主进程产生随机数,并传给子进程
memset(sums,0,sizeof(int)*PRO_NUM);
int data[NUM]; //用于存储子进程从主进程接收到的数据
memset(data,0,sizeof(int)*NUM);
create_pipe_table(fdtable);
while(1)
{
//每次循环创建进程
pid = fork();
if(pid > 0 ) //父进程
{
if( i >= PRO_NUM - 1 )
{
for(i = 0;i<PRO_NUM;++i)
{
create_rand(&_data,i);
write(fdtable[i].main_to_sub_fd[WRITE_END],_data,sizeof(int)*NUM); //向管道中写入随机数
}
for(i = 0;i<PRO_NUM;++i)
{
read(fdtable[i].sub_to_main_fd[READ_END],&sums[i],sizeof(int)); //从子进程管道中读出数据
total_sum = total_sum + sums[i];
}
printf("The total sum is %d\n",total_sum);
//主进程结束,在此等待
while(1)
{
printf("the parent process is waiting ....\n");
sleep(3);
};
}
}
else //子进程
{
printf("this is the %d process.\n",i);
flag = i;
break;
}
++i;
}
//子进程执行
child_pro(data,flag);
return 0;
}
总结:这个程序写了好久,一开始有个问题一直得不到解决。就是主进程接受到的结果老是不正确,总是其发送给子进程的第一个数据。而子进程接收到的数据,也是从主进程发送的第二个数据开始的。
折腾了好几天,终于弄清楚了。还是对于管道没理解清楚。每创建一个管道,就会产生两个描述符,只要得到了这两个描述符,就能从相应的管口进行读写。本程序原来对于每个程序只维护了一个管道,本来想的是,主进程向这个管道写入原始数据,子进程从这个管道中读入原始数据。子进程计算后,将结果在通过这个管道写入,然后主进程将结果读出。感觉是没什么问题,但是看程序就知道,主进程在向每个子进程写入数据后,就一直轮循向管道中读入数据,这个时候如果子进程还没将结果写入管道,那么主进程读入的结果就是错误的。(原来犯得错误就是主进程读的数据是自己原来写入的第一个数据)。我去,扯了这么多,突然想到可以用一句话代替啊,就是管道是半双工通信的。傻了我。
找到了错误原因,改起来就方便了,对每个子进程和主进程之间的通信又加了一对管道,即使用两组管道就可以了。