为什么这个多线程程序无法写入第二个文件?

huangapple go评论79阅读模式
英文:

Why is this multi-threaded program fails to write to the second file?

问题

我试图编写一个程序,该程序获取最多10个包含学生姓名和他们的成绩行的`.txt`文件的输入。该程序应该为每个输入文件创建一个新进程,并将每个学生的姓名和平均成绩写入一个临时文件,该临时文件以该进程的pid命名

最终,父进程应等待所有进程,并创建一个新进程将所有临时文件合并成一个组合输出文件。我曾经努力正确传递pids到输出文件创建部分,所以我总是只得到一个文件成功写入,另一个没有(在运行两个输入文件时)。

基本上,每次都会缺少一个pid,因此我的程序试图读取一个不存在的文件。我很难理解为什么会发生这种情况以及可能的修复方法。任何帮助将不胜感激。
英文:

I trying to write a program that gets input of up to 10 .txt files containing lines of student names and their grades. The program is supposed to create a new process for each input file and write to each student's name and average grade to a temporary file named after the pid of the process.

Eventually, the parent should wait for all processes and create a new process to combine all temporary files into a combined output file. I had struggled to pass over the pids correctly down to the output file creation so I always get only one file written successfully and the other not (when running on two input files).

Basically one of the pids is missing each time therefore my program tries to read an inexistent file. I struggling to understand why this happens and of a possible fix. Any help will be appreciated.

Program:

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

#define GRADES_FILE "all_std.log"

typedef struct {
    char name[11];
    int grades[256];
    int num_grades;
} student_t;

void report_data_summary(int num_stud) {
    fprintf(stderr, "grade calculation for %d students is done\n", num_stud);
}

void calculate_students_grade_average_from_input(int file_number, char* filenames[])
{
    student_t students[256];
    int num_students = 0;
    char filename[50];
    sprintf(filename, "%d.temp", getpid());

    FILE* fp_in = fopen(filenames[file_number], "r");
    if (fp_in == NULL) {
        printf("Could not open file %s\n", filenames[file_number]);
        exit(EXIT_FAILURE);
    }

    while (!feof(fp_in)) {
        fscanf(fp_in, "%s", students[num_students].name);
        int grade;
        students[num_students].num_grades = 0;
        while (fscanf(fp_in, "%d", &grade) == 1) {
            students[num_students].grades[students[num_students].num_grades] = grade;
            students[num_students].num_grades++;
        }
        num_students++;
    }
    fclose(fp_in);

    FILE* fp_out = fopen(filename, "w");
    if (fp_out == NULL) {
        printf("Could not open output file %s\n", filename);
        return;
    }

    for (int j = 0; j < num_students; ++j) {
        int sum = 0;
        for (int k = 0; k < students[j].num_grades; ++k) {
            sum += students[j].grades[k];
        }
        float avg = (float)sum / students[j].num_grades;
        fprintf(fp_out, "%s %.1f\n", students[j].name, avg);
    }
    fclose(fp_out);

    fprintf(stderr, "process: %d file: %s number of students: %d\n", getpid(), filenames[file_number], num_students);
}


void create_output(int file_count, pid_t* temp_processes[])
{
    int total_students = 0;
    pid_t pid = fork();

    if (pid < 0) {
        perror("fork failed");
        exit(EXIT_FAILURE);
    }
    else if (pid == 0) {
        FILE* fp_final = fopen(GRADES_FILE, "w");
        if (fp_final == NULL) {
            perror("Could not open final output file");
            exit(EXIT_FAILURE);
        }

        for (int i = 0; i < file_count; ++i) {
            char temp_filename[16];
            sprintf(temp_filename, "%d.temp", temp_processes[i]);
            
            FILE* fp_temp = fopen(temp_filename, "r");
            if (fp_temp == NULL) {
                printf("Could not open temporary file %s\n",temp_filename);
                return;
            }

            char line[101];
            while (fgets(line, sizeof(line), fp_temp)) {
                fputs(line, fp_final);
                total_students++;
            }

            report_data_summary(total_students);
            fclose(fp_temp);
        }

        fclose(fp_final);
        return;
    }
    else {
        wait(NULL);
    }
}




void ex01(int file_count, char* filenames[])
{
    pid_t temp_processes[10];
    pid_t pid;

    for (int i = 1; i < file_count; ++i) {
        pid = fork();

        if (pid < 0) {
            perror("fork failed");
            exit(EXIT_FAILURE);
        }
        else if (pid > 0) {
            temp_processes[i-1] = pid;
            printf("%d = %d\n",0,temp_processes[0]);
            printf("%d = %d\nend\n",1,temp_processes[1]);
        }
        else {
            calculate_students_grade_average_from_input(i, filenames);
            return;
        }
    }

    for (int i = 1; i < file_count; ++i) {
        wait(NULL);
    }
    

    if (pid > 0)
    {
        create_output(file_count - 1, temp_processes);
    }
}

int main(int argc, char* argv[]) {
    ex01(argc, argv);
    return 0;
}

Input:

file1.txt:
Abraham 80 90 75
Benny 90
Garland 70 9   90 100

file2.txt:
Dana 90 95
Ron 100 80 90

Output file:

Abraham 81.7
Benny 90.0
Garland 67.2

Output missing the second file's students.

Console output:

0 = 35222
1 = 0
end
0 = 35222
1 = 35223
end
process: 35223 file: gr_2.txt number of students: 2
process: 35222 file: gr_1.txt number of students: 3
grade calculation for 3 students is done
Could not open temporary file 0.temp

答案1

得分: 2

在这一行中:

```c
void create_output(int file_count, pid_t* temp_processes[])

你定义了函数的第二个参数的类型为 pid_t **。然而,在函数 ex01 中,你却这样调用了函数 create_output

create_output(file_count - 1, temp_processes);

表达式 temp_processes衰变&temp_processes[0],它的类型是 pid_t*。因此,你的程序正在引发未定义行为,因为第二个参数的类型不匹配。

你的编译器应该会警告你。这是我从gcc得到的警告:

<source>: 在函数‘ex01’中:
<source>:142:39: 警告:从不兼容的指针类型传递第2个参数给‘create_output’ [-Wincompatible-pointer-types]
  142 |         create_output(file_count - 1, temp_processes);
      |                                       ^~~~~~~~~~~~~~
      |                                       |
      |                                       pid_t * {aka int *}
<source>:65:43: 注意:期望‘pid_t **’ {aka ‘int **’}但参数的类型是‘pid_t *’ {aka ‘int *’}
   65 | void create_output(int file_count, pid_t* temp_processes[])
      |                                    ~~~~~~~^~~~~~~~~~~~~~~~

我建议你将这一行:

void create_output(int file_count, pid_t* temp_processes[])

改成:

void create_output(int file_count, pid_t temp_processes[])
英文:

In the line

void create_output(int file_count, pid_t* temp_processes[])

you defined the second argument of the function to be of type pid_t **. However, in the function ex01, you are calling the function create_output like this:

create_output(file_count - 1, temp_processes);

The expression temp_processes will decay to &amp;temp_processes[0], which is of type pid_t*. Therefore, your program is invoking undefined behavior, because the types of the second parameter do not match.

Your compiler should be warning you about this. This is the warning I get from gcc:

&lt;source&gt;: In function &#39;ex01&#39;:
&lt;source&gt;:142:39: warning: passing argument 2 of &#39;create_output&#39; from incompatible pointer type [-Wincompatible-pointer-types]
  142 |         create_output(file_count - 1, temp_processes);
      |                                       ^~~~~~~~~~~~~~
      |                                       |
      |                                       pid_t * {aka int *}
&lt;source&gt;:65:43: note: expected &#39;pid_t **&#39; {aka &#39;int **&#39;} but argument is of type &#39;pid_t *&#39; {aka &#39;int *&#39;}
   65 | void create_output(int file_count, pid_t* temp_processes[])
      |                                    ~~~~~~~^~~~~~~~~~~~~~~~

I suggest that you change the line

void create_output(int file_count, pid_t* temp_processes[])

to:

void create_output(int file_count, pid_t temp_processes[])

huangapple
  • 本文由 发表于 2023年7月24日 00:31:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/76749291.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定