如何确保子进程在父进程完成创建循环后才执行?
如何让父进程完成所有创建输出后再启动子进程执行逻辑
我用C语言写了一个for循环来创建指定数量的进程,父进程分支(pid>0)和子进程分支(pid==0)都在循环内,wait()调用放在循环结束后。目前存在问题:部分子进程会在父进程仍处于循环执行阶段时就输出fprintf内容,且子进程启动顺序随机。需要确保父进程完成整个循环及所有输出后,子进程才开始执行各自的逻辑。
执行命令./a.out 5 process 1后,输出对比如下:
输出对比
预期输出
Process 0 creating process 1... Process 0 creating process 2... Process 0 creating process 3... Process 0 creating process 4... Process 0 creating process 5... Process 1 beginning... *(随机时长休眠)* Process 1 ending... Process 2 beginning... *(随机时长休眠)* Process 2 ending... Process 3 beginning... *(随机时长休眠)* Process 3 ending... Process 4 beginning... *(随机时长休眠)* Process 4 ending... Process 5 beginning... *(随机时长休眠)* Process 5 ending...
实际输出
Process 0 creating process 1... Process 1 beginning... Process 0 creating process 2... Process 2 beginning... Process 0 creating process 3... Process 0 creating process 4... Process 0 creating process 5... Process 5 beginning... Process 3 beginning... Process 4 beginning... *(随机时长休眠)* Process 1 ending... Process 2 ending... Process 5 ending... Process 3 ending... Process 4 ending...
现有代码
main.c
/* * Write a program that takes three arguments: * the number of “things” (between 1 and 256), * a word: either “thread” or “process, and * a “pattern number” (1-2 only). */ #include "functions.c" #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/wait.h> #include <unistd.h> // USE STDERR FOR PRINTING // fprintf(stderr, "This is process %d \n", getpid()); // extern set_pattern typedef struct { int number; pid_t pid; } process; typedef struct { int number; pthread_t tid; } thread; int main(int argc, char *argv[]) { setbuf(stdout, NULL); // Check for correct amount of paramters if (argc != 4) { help_and_exit(argv[0]); } // Parse parameters and check for errors // Set size int size; set_size(argv, &size); // Set type (thread or process) exec_t type; set_choice(argv, &type); // Set pattern int pattern; set_pattern(argv, &pattern); // printf("Size is %d, choice is %d, and pattern is %d\n", size, type, // pattern); if (pattern == 1) { if (type == Process) { // process processes[size]; pid_t alpha_process = getpid(); int process_number = 0; pid_t pids[size]; int n = size; int flag = 0; for (int i = 0; i < n; i++) { fflush(stdout); if ((pids[i] = fork()) < 0) { perror("fork"); } else if (pids[i] == 0) { fprintf(stderr, "Process %d beginning...\n", i + 1); sleep(rand() % 5); fflush(stdout); fprintf(stderr, "Process %d ending...\n", i + 1); exit(0); } else if (pids[i] > 0) { fprintf(stderr, "Process %d creating process %d...\n", i - i, i + 1); } } int counter = 0; while (counter < n) { wait(NULL); counter++; } } } return 0; }
functions.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/wait.h> #include <unistd.h> typedef enum { Thread, Process } exec_t; void help_and_exit(char *name) { fprintf(stderr, "usage: %s <number of things> {thread | process} <pattern number>\n", name); exit(-1); } void set_size(char *argv[], int *size) { if (sscanf(argv[1], "%d", size) == 0) { fprintf(stderr, "First parameter must be an integer. Recieved: '%s'\n", argv[1]); help_and_exit(argv[0]); } if (*size > 256 || *size < 1) { fprintf(stderr, "Number of things must be between 1-256!\n"); help_and_exit(argv[0]); } } void set_choice(char *argv[], exec_t *type) { char choice[8]; if (sscanf(argv[2], "%s", choice) == 0) { fprintf(stderr, "Second parameter must be a string. Recieved: '%s'\n", argv[2]); help_and_exit(argv[0]); } if (strcmp(choice, "thread") != 0 && strcmp(choice, "process") != 0) { fprintf(stderr, "Second parameter must be either 'thread' or 'process'\n"); help_and_exit(argv[0]); } else if (strcmp(choice, "thread") == 0) { *type = Thread; } else { *type = Process; } } void set_pattern(char *argv[], int *pattern) { if (sscanf(argv[3], "%d", pattern) == 0) { fprintf(stderr, "Third parameter must be an integer. Recieved: '%s'\n", argv[3]); help_and_exit(argv[0]); } if (*pattern != 1 && *pattern != 2) { fprintf(stderr, "Pattern number must be either 1 or 2!\n"); help_and_exit(argv[0]); } }
解决方案
问题根源是fork()后子进程会立即被操作系统调度执行,与父进程抢占CPU时间片。要实现父进程完成所有创建输出后再启动子进程,可通过管道实现进程间同步,这是最简单的方案:
修改思路
- 父进程在创建子进程前,先创建一个匿名管道。
- 每个子进程被创建后,先尝试从管道读取数据(管道无数据时会阻塞)。
- 父进程完成所有创建输出后,向管道写入任意数据,触发所有子进程的读取操作结束,开始执行自身逻辑。
修改后的main.c关键代码
替换原有的Process分支逻辑:
if (pattern == 1) { if (type == Process) { pid_t pids[size]; int n = size; int pipe_fd[2]; // 创建匿名管道 if (pipe(pipe_fd) == -1) { perror("pipe"); exit(EXIT_FAILURE); } for (int i = 0; i < n; i++) { fflush(stderr); // 确保父进程输出立即刷新,避免缓存问题 if ((pids[i] = fork()) < 0) { perror("fork"); } else if (pids[i] == 0) { close(pipe_fd[1]); // 子进程关闭管道写端 char dummy; read(pipe_fd[0], &dummy, 1); // 阻塞等待父进程信号 close(pipe_fd[0]); fprintf(stderr, "Process %d beginning...\n", i + 1); sleep(rand() % 5); fprintf(stderr, "Process %d ending...\n", i + 1); exit(0); } else if (pids[i] > 0) { fprintf(stderr, "Process %d creating process %d...\n", 0, i + 1); } } // 父进程完成所有创建,发送唤醒信号 close(pipe_fd[0]); // 关闭管道读端 write(pipe_fd[1], "1", 1); // 写入一个字节,唤醒所有子进程 close(pipe_fd[1]); int counter = 0; while (counter < n) { wait(NULL); counter++; } } }
说明
- 管道的
read()操作在无数据时会阻塞,子进程会卡在该步骤直到父进程写入数据。 - 父进程完成所有创建输出后,向管道写入数据,所有子进程会被唤醒并开始执行后续逻辑。
- 必须在父进程和子进程中正确关闭管道的无用端,避免资源泄漏。
内容的提问来源于stack exchange,提问作者Nolan
相关产品推荐
相关产品推荐

