利用无名管道实现简单并行计算

来源:互联网 发布:srs音效增强软件 编辑:程序博客网 时间:2024/05/20 11:22

名称:利用无名管道实现简单并行计算
说明:本程序利用无名管道进行进程间通信,达到多进程同时计算的目的。
基本算法是:首先在主进程里创建了一定数量的子进程,然后主进程通过无名管道向子进程传递要计算的数据,子进程计算后,通过无名管道将计算的结果(此处是求和)返回给主进程,主进程进行汇总。

#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 / 4typedef 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;}

总结:这个程序写了好久,一开始有个问题一直得不到解决。就是主进程接受到的结果老是不正确,总是其发送给子进程的第一个数据。而子进程接收到的数据,也是从主进程发送的第二个数据开始的。
折腾了好几天,终于弄清楚了。还是对于管道没理解清楚。每创建一个管道,就会产生两个描述符,只要得到了这两个描述符,就能从相应的管口进行读写。本程序原来对于每个程序只维护了一个管道,本来想的是,主进程向这个管道写入原始数据,子进程从这个管道中读入原始数据。子进程计算后,将结果在通过这个管道写入,然后主进程将结果读出。感觉是没什么问题,但是看程序就知道,主进程在向每个子进程写入数据后,就一直轮循向管道中读入数据,这个时候如果子进程还没将结果写入管道,那么主进程读入的结果就是错误的。(原来犯得错误就是主进程读的数据是自己原来写入的第一个数据)。我去,扯了这么多,突然想到可以用一句话代替啊,就是管道是半双工通信的。傻了我。
找到了错误原因,改起来就方便了,对每个子进程和主进程之间的通信又加了一对管道,即使用两组管道就可以了。