【Linux】【C/C++】多进程协同词频统计

本文介绍了在Linux环境下,使用多进程协同统计指定目录下文本文件的词频。程序设计包括一个父进程和多个子进程,父进程负责任务划分和汇总,子进程执行文件读取与词频统计。通过临时文件实现父子进程间通信,测试结果显示程序能有效并行处理多个任务。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在Linux环境下实现对指定目录下的文本文件进行单词词频的统计。由于可能会涉及到很多文件,因此为了提高统计效率,采用多进程协同合作的方式实现词频统计。

目标

  1. 实现多个进程之间系统并行运行,保证执行结果的正确及高效
  2. 进程之间任务是不同的,包含两大类进程,父进程进行任务划分及汇总,子进程负责完成划分的任务

设计思路及实现

程序实现的过程中,一共有11个进程并行执行,其中使用的是10个统计进程和一个父进程,子进程的创建数目可以根据宏定义进行修改。父进程的任务分为两个阶段:
1. 统计开始前,根据输入的路径进行遍历,获取所有的给定路径及其子目录下满足条件的文件,并将这些文件的绝对路径存储在一个任务队列中,用于向子进程分发任务。
2. 统计开始后,等待子进程,若子进程完成所有分配给它的任务并正常退出之后,父进程开始汇总相应退出子进程的统计结果,直到所有的子进程都正常退出并且所有的汇总工作都完成之后,父进程输出汇总结果,结束程序的运行。

子进程的执行过程就比较单一,仅仅是根据父进程分配给自己的文件绝对路径,去打开这些文件并读取和统计,直到所有分配的文件都统计完成之后,该子进程就完成任务正常退出。

根据上述的设计思路,可以看出,关键问题在于任务的分配和父子进程间的同步以及信息交换。
在任务分配中,以文件为基本的分配单位,且假设文件是多于统计进程的数目,那么每个统计进程至少可以分配到1个文件。实现时是通过父进程的遍历,将文件绝对路径存储在一个共享vector变量中,这样所有的子进程都可以访问这个共享向量。父进程最后通过这个共享向量实现任务的均衡划分。
父进程主要完成两个方面的任务:一个是文件的遍历,一个是子进程统计结果的汇总。父进程的执行流程图如下:

任务划分过程代码:task_load 是通过遍历得到的所有的文件数目取整加1.
那么子进程所需要处理的任务数量就是在vector中从i*task\_load 一直到end 之间的所有文件,其中end的取值是所有文件数目与(i+1)*task\_load中的较小值。
txt_files.size() < (i+1)*task_load ? txt_files.size():(i+1)*task_load
这样就可以保证vector中的所有任务都得到了处理并且不会出现交叠和越界的情况。

示例代码如下:

    int task_load = txt_files.size()/PROCESS_NUM + 1;

    for(i=0;i<PROCESS_NUM;i++){
        //子进程执行代码片段
        if(fork() == 0){
            int exit_code = i;//getpid();
            //每个子进程的任务量为:txt_files向量中位于 i*task_load 到 end之间的文件;
            //其中end是(i+1)*task_load与txt_files.size()之间的较小值
            int end = txt_files.size() < (i+1)*task_load ? txt_files.size():(i+1)*task_load;
            DICT child_dict;
            //char *wd;
            char wd[WORD_LEN];
            char buffer[BUFFER_SIZE + 1];
            int r_fd;
            int nread;
            char get_file_path[FILE_PATH_LEN];
            for(int j = i*task_load;j< end;j++){
                memset(get_file_path,0,FILE_PATH_LEN);
                int cpy_len = txt_files[j].length();
                txt_files[j].copy(get_file_path,cpy_len,0);
                //cout<<txt_files[j]<<" opened in process "<<i<<endl;
                //get_file_path[]末尾补'\0'的操作可要可不要
                //get_file_path[strlen(get_file_path)] = '\0';
                cout<<"child "<<i<<" "<<get_file_path<<endl;
                r_fd = open(get_file_path,O_RDONLY);
                if(r_fd == -1 ){
                    printf("open file failed!\n");
                    exit(EXIT_FAILURE);
                }
                memset(buffer,0,BUFFER_SIZE);
                nread = read(r_fd,buffer,BUFFER_SIZE);

                if(nread < 0){
                    printf("read failed \n");
                    exit(EXIT_FAILURE);
                }

                //new
                int index = 0;
                int buf_len = strlen(buffer);
                int word_len =0;
                char temp_word[WORD_LEN];
                bool flag = false;
                while(nread > 0){
                    memset(wd,0,WORD_LEN);
                    while(index < buf_len){

                        if(flag){
                            //若出现单词分两次读取的情况
                            //一般单词被隔断,那么第二部分首字母一般不会是大写
                            //此处未对大小写进行判断
                            while(is_letter(buffer[index]) > 0){
                                if(is_letter(buffer[index]) == 1){
                                    wd[word_len] = buffer[index];
                                }else
                                    wd[word_len] = tolower(buffer[index]);
                                word_len++;
                                index++;
                            }
                            wd[word_len] = '\0';
                            strcat(temp_word,wd);
                            child_dict.add(temp_word,1);
                            memset(temp_word,0,WORD_LEN);
                            word_len = 0;
                            index++;
                            flag = false;
                        }

                        if(is_letter(buffer[index]) == 1){
  
  //小写字母
                            wd[word_len] = buffer[index];
                            index++;
                            word_len++;
                        }else if(is_letter(buffer[index]) == 2){
  
  //大写字母
                            wd[word_len] = tolower(buffer[index]);
                            index++;
                            word_len++;
                        }else{
  
  //不在字母表中
                            wd[word_len+1] = '\0';
                            if(strlen(wd) > 0){
                                child_dict.add(wd,1);
                            }
                            memset(wd,0,WORD_LEN);
                            word_len = 0;
                            index++;
                        }
                    }
                    index = 0;
                    memset(buffer,0,BUFFER_SIZE);
                    nread = read(r_fd,buffer,BUFFER_SIZE);
                    buf_len = strlen(buffer);

                    if(word_len != 0){
                        //buffer数据读完,但是没有到达单词尾部
                        //单词被分成两次读到buffer中,需要做特殊处理
                        flag = true;
                        strcat(temp_word,wd);
                        word_len = 0;
                    }
                }
                close(r_fd);

                nread = 0;
                r_fd = -1;
                //wd = NULL;//old
                memset(wd,0,WORD_LEN);
            }
            //输出子进程的词频结果
            printf("child %d result: ",i);
            child_dict.display();

            //统计完需要完成的所有文件内容自后,将统计内容序列化到临时文件;
            //文件名从全局变量temp_files向量中获取

            int c_len = temp_files[i].length();
            //将temp_files[i]中的内容复制到get_file_path中
            //用于将子进程比那里词频写入到临时文
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值