Проблема с чтением нескольких файлов параллельно с использованием нескольких процессов: pipe (), for () и wait () в C - PullRequest
0 голосов
/ 07 марта 2020

Цель кода - найти общее количество всех целых чисел в нескольких файлах, вычислив сумму каждого файла параллельно. Есть ли проблемы с моим каналом, потому что конечный результат всегда равен 0?

Код скопирован с обновленного Google Do c, на который есть ссылки в комментариях.

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<string.h>
#include<sys/wait.h>
int main(int argc, char *argv[]) {
   double A[2], B[2];
   double count;
   int i = 1;
   double num;
   double sum = 0;
   int fd1[argc][2];
   double total = 0;
   int num_files = argc - 1;
   double ssum, res;

   for (i = 1; i <= num_files; i++) {
       pipe(fd1[i]);
   }
   for (i = 1; i <= num_files; i++) {
       int pid = fork();
       if (pid == 0) {
           FILE *fp = fopen(argv[i], "r");
           printf("Processing file named %s\n", argv[i]);
           if (fp == NULL) {
               printf("File is empty %s\n", argv[i]);
               exit(1);
           }
           while ((res = fscanf(fp, "%lf", &num) != EOF)) {
               A[0] +=num;
               A[1]++;
           }
           fclose(fp);
           write(fd1[i][1], &A, (sizeof(A) +1));
           exit(0);
       } else
           wait(NULL);
   }
   for (i = 1; i <= num_files; i++) {
       read(fd1[i][0], &B, 50);
       if (B[1] == 0){
           printf("Error\n");
           exit(0);
       }

       ssum += B[0];
       count+= B[1];
   }
   printf("%lf\n", ssum / count);
   return 0;
}

1 Ответ

0 голосов
/ 08 марта 2020

Вопрос говорит «сумма целых чисел», но это оставляет открытым вопрос «тогда почему на земле код использует double значения везде»? Этот ответ предполагает, что данные должны быть значениями с плавающей запятой, но он будет работать аналогично целочисленным данным. Примеры могут быть переработаны для использования целочисленных данных. Однако было бы более разумно пересмотреть код так, чтобы он подсчитывал и суммировал, используя целые числа - если только файлы не будут такими большими, что целые числа (например, unsigned long long) больше не смогут точно хранить значения. На этом этапе может быть какая-то ценность в использовании double, но это становится определенно хитрым - и это вряд ли будет проблемой.

Вы приближаетесь, но вы Вы по-прежнему не инициализируете все, что требует инициализации, у вас все еще есть неиспользуемые переменные, вы все еще не выполняете суммы параллельно. Вот код с исправленными проблемами. Также сообщается о сумме, числе и среднем значении каждого ребенка; он сообщает общую сумму и количество, а также общее среднее. Это помогает с проверкой. Дочерние сообщения содержат свой PID. Код закрывает неиспользуемые концы каналов и закрывает используемые концы, когда они заканчиваются. Я также локализовал переменные гораздо больше.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
    int fd[argc][2];

    for (int i = 1; i < argc; i++)
    {
        pipe(fd[i]);
    }

    for (int i = 1; i < argc; i++)
    {
        int pid = fork();
        if (pid == 0)
        {
            double A[2] = { 0.0, 0.0 };
            FILE *fp = fopen(argv[i], "r");
            close(fd[i][0]);
            printf("%d: Processing file named %s\n", (int)getpid(), argv[i]);
            if (fp == NULL)
            {
                fprintf(stderr, "%d: File is empty %s\n", (int)getpid(), argv[i]);
                exit(1);
            }
            double num;
            while (fscanf(fp, "%lf", &num) == 1)
            {
                A[0] += num;
                A[1]++;
            }
            fclose(fp);
            write(fd[i][1], &A, sizeof(A));
            printf("%d: (%lg / %lg) = %lg\n", (int)getpid(), A[0], A[1], A[0] / A[1]);
            close(fd[i][1]);
            exit(0);
        }
        else
            close(fd[i][1]);
    }

    for (int i = 1; i < argc; i++)
    {
        int status;
        int corpse = wait(&status);
        if (corpse == -1)
            break;
        printf("Child %d exited with status 0x%.4X\n", corpse, status);
    }

    double count = 0.0;
    double ssum = 0.0;
    for (int i = 1; i < argc; i++)
    {
        double B[2];
        read(fd[i][0], &B, sizeof(B));
        if (B[1] == 0)
        {
            fprintf(stderr, "Error reading pipe %d\n", i);
            exit(0);
        }
        ssum += B[0];
        count += B[1];
        close(fd[i][0]);
    }

    printf("(%lg / %lg) = %lf\n", ssum, count, ssum / count);
    return 0;
}

Я создал 10 файлов со случайным числом строк от 10 до 20 в каждом, с одним числом в диапазоне от 10 до 100. Это слишком много информации, чтобы показать здесь. Программа была pipe29 создана из pipe29.c.

$ pipe29 data.?
27911: Processing file named data.0
27912: Processing file named data.1
27913: Processing file named data.2
27912: (675 / 12) = 56.25
27911: (937.1 / 17) = 55.1235
27913: (792.9 / 13) = 60.9923
27914: Processing file named data.3
27915: Processing file named data.4
27915: (1051.8 / 18) = 58.4333
27914: (659.1 / 12) = 54.925
27916: Processing file named data.5
27917: Processing file named data.6
27916: (756.5 / 12) = 63.0417
27918: Processing file named data.7
27917: (1153.5 / 20) = 57.675
Child 27915 exited with status 0x0000
Child 27914 exited with status 0x0000
Child 27916 exited with status 0x0000
Child 27913 exited with status 0x0000
Child 27917 exited with status 0x0000
Child 27912 exited with status 0x0000
Child 27911 exited with status 0x0000
27919: Processing file named data.8
27918: (986.6 / 16) = 61.6625
Child 27918 exited with status 0x0000
27920: Processing file named data.9
27919: (1093 / 19) = 57.5263
27920: (1314 / 19) = 69.1579
Child 27919 exited with status 0x0000
Child 27920 exited with status 0x0000
(9419.5 / 158) = 59.617089
$

Я запустил программу статистики по файлам, и она дала:

$ pstats data.?
# Count    = 158
# Sum(x1)  =  9.419500e+03
# Sum(x2)  =  6.619076e+05
# Mean     =  5.961709e+01
# Std Dev  =  2.528115e+01 (sample)
# Variance =  6.391366e+02 (sample)
# Min      =  1.080000e+01
# Max      =  1.000000e+02
$

Сумма, количество и среднее согласны, что обнадеживает. Интересно, что один из файлов может содержать 100.0 (максимум, о котором сообщает pstats). Учитывая диапазон данных, вы ожидаете среднее значение, близкое к 60, и это то, что рассчитывается.

Обратите внимание, что было бы допустимо и целесообразно использовать одну трубу. Сообщения, которые пишут дети, настолько малы, что они будут написаны атомарно - поэтому разные дети не будут мешать данным друг друга в канале.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
    int fd[2];

    pipe(fd);

    for (int i = 1; i < argc; i++)
    {
        int pid = fork();
        if (pid == 0)
        {
            double A[2] = { 0.0, 0.0 };
            FILE *fp = fopen(argv[i], "r");
            close(fd[0]);
            printf("%d: Processing file named %s\n", (int)getpid(), argv[i]);
            if (fp == NULL)
            {
                fprintf(stderr, "%d: File is empty %s\n", (int)getpid(), argv[i]);
                exit(1);
            }
            double num;
            while (fscanf(fp, "%lf", &num) == 1)
            {
                A[0] += num;
                A[1]++;
            }
            fclose(fp);
            write(fd[1], &A, sizeof(A));
            printf("%d: (%lg / %lg) = %lg\n", (int)getpid(), A[0], A[1], A[0] / A[1]);
            close(fd[1]);
            exit(0);
        }
    }
    close(fd[1]);

    for (int i = 1; i < argc; i++)
    {
        int status;
        int corpse = wait(&status);
        if (corpse == -1)
            break;
        printf("Child %d exited with status 0x%.4X\n", corpse, status);
    }

    double count = 0.0;
    double ssum = 0.0;
    for (int i = 1; i < argc; i++)
    {
        double B[2];
        read(fd[0], &B, sizeof(B));
        if (B[1] == 0)
        {
            fprintf(stderr, "Error reading pipe %d\n", i);
            exit(0);
        }
        ssum += B[0];
        count += B[1];
        printf("%d: (%lg / %lg) = %lg\n", (int)getpid(), B[0], B[1], B[0] / B[1]);
    }
    close(fd[0]);

    printf("(%lg / %lg) = %lf\n", ssum, count, ssum / count);
    return 0;
}

Пример вывода:

$ pipe31 data.?
27965: Processing file named data.0
27966: Processing file named data.1
27967: Processing file named data.2
27966: (675 / 12) = 56.25
27965: (937.1 / 17) = 55.1235
27968: Processing file named data.3
27967: (792.9 / 13) = 60.9923
27968: (659.1 / 12) = 54.925
27969: Processing file named data.4
27970: Processing file named data.5
27969: (1051.8 / 18) = 58.4333
27970: (756.5 / 12) = 63.0417
27971: Processing file named data.6
27972: Processing file named data.7
27971: (1153.5 / 20) = 57.675
27972: (986.6 / 16) = 61.6625
Child 27970 exited with status 0x0000
Child 27969 exited with status 0x0000
Child 27968 exited with status 0x0000
Child 27967 exited with status 0x0000
Child 27966 exited with status 0x0000
Child 27965 exited with status 0x0000
27973: Processing file named data.8
Child 27972 exited with status 0x0000
Child 27971 exited with status 0x0000
27974: Processing file named data.9
27973: (1093 / 19) = 57.5263
Child 27973 exited with status 0x0000
27974: (1314 / 19) = 69.1579
Child 27974 exited with status 0x0000
27964: (675 / 12) = 56.25
27964: (937.1 / 17) = 55.1235
27964: (792.9 / 13) = 60.9923
27964: (659.1 / 12) = 54.925
27964: (1051.8 / 18) = 58.4333
27964: (756.5 / 12) = 63.0417
27964: (1153.5 / 20) = 57.675
27964: (986.6 / 16) = 61.6625
27964: (1093 / 19) = 57.5263
27964: (1314 / 19) = 69.1579
(9419.5 / 158) = 59.617089
$

Все данные печатаются согласованно, что обнадеживает.

...