即使我在父进程中使用sleep(),我的子进程也会在最后执行

chh*_*chh 5 c parallel-processing sleep fork process

有人可以解释为什么父进程总是在子进程中的while循环开始之前完全完成,即使我让父进程休眠.

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

int main(int argc, char **argv) {
    int i = 10000, pid = fork();
    if (pid == 0) {
        while(i > 50) {
            if(i%100==0) {
                sleep(20);
            }
            printf("Child: %d\n", i);
            i--;
        }
    } else {
        while(i < 15000) {
            if(i%50==0) {
                sleep(50);
            }
            printf("Parent: %d\n", i);
            i++;
        }
    }
    exit(EXIT_SUCCESS);
}
Run Code Online (Sandbox Code Playgroud)

输出如下所示:

Parent: ..
Parent: ..
Parent: ..
Run Code Online (Sandbox Code Playgroud)

直到Parent完成,然后对于Child.

原因可能是我在单核CPU上进行测试?如果我有多核设置,结果会改变吗?sleep(50)肯定有效 - 因为脚本完成需要很长时间 - 为什么CPU不会切换进程?是否存在例如在while循环期间进程对CPU具有"更多"或独占权限的情况?

感谢帮助.:)

小智 3

是否存在诸如在 while 循环期间进程对 CPU 具有“更多”或独占权利的情况?

好吧,这还没有定义,但那就太疯狂了。

我无法重现你的问题。将睡眠时间减少到2几秒5(这样我就不必永远等待),正如人们所期望的那样,孩子首先在这里被解除阻塞。(AMD64 上的 Debian 8、Linux 3.16.1-ck1 [BFS 调度程序,非标准])

我想说你的调度程序的行为非常奇怪,可能只是被破坏了。但话虽这么说,依赖调度程序的任何特定行为从来都不是一个好主意。总是假设它已经损坏并且疯狂了——如果你的代码允许特定的执行顺序,就会一个足够疯狂的调度程序来选择它。(*)

因此,每当您需要依赖某些同步时,请使用同步原语(semaphores例如,mutexes具有用于不同进程的共享pipes版本——您也可以只在某些情况下使用)。

编辑:添加两个用于同步进程的示例。

第一个版本 (ab) 使用pipes:

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

int main(int argc, char **argv) {
    int i = 10000;
    int parent_done[2];
    int child_done[2];
    char dummy[1] = { 0 };
    int pid;

    pipe(parent_done);
    pipe(child_done);

    /* stdio buffering would lead to intermingled output */
    setvbuf(stdout, 0, _IONBF, 0);

    pid = fork();

    if (pid == 0) {
        close(parent_done[1]);
        close(child_done[0]);

        while(i > 50) {
            if(i%100==0) {
                if (i < 10000) write(child_done[1], dummy, 1);
                read(parent_done[0], dummy, 1);
            }
            printf("Child: %d\n", i);
            i--;
        }
    } else {
        close(parent_done[0]);
        close(child_done[1]);

        while(i < 15000) {
            if(i%50==0) {
                write(parent_done[1], dummy, 1);
                read(child_done[0], dummy, 1);
            }
            printf("Parent: %d\n", i);
            i++;
        }
    }
    exit(EXIT_SUCCESS);
}
Run Code Online (Sandbox Code Playgroud)

然后使用 POSIX 信号量进行相同的操作(恕我直言,这更干净,因为信号量旨在用于同步):

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

struct semaphores
{
    sem_t child_done;
    sem_t parent_done;
};

int main(int argc, char **argv) {
    int i = 10000;
    int pid;

    /* map shared memory for the semaphores */
    struct semaphores *sems = mmap(0, sizeof(*sems), PROT_READ|PROT_WRITE,
            MAP_SHARED|MAP_ANONYMOUS, -1, 0);

    /* initialize both semaphores as "shared" and with an initial count
     * of 0 */
    sem_init(&sems->parent_done, 1, 0);
    sem_init(&sems->child_done, 1, 0);

    /* stdio buffering would lead to intermingled output */
    setvbuf(stdout, 0, _IONBF, 0);

    pid = fork();

    if (pid == 0) {
        while(i > 50) {
            if(i%100==0) {
                if (i < 10000) sem_post(&sems->child_done);
                sem_wait(&sems->parent_done);
            }
            printf("Child: %d\n", i);
            i--;
        }
        sem_post(&sems->child_done);
    } else {
        while(i < 15000) {
            if(i%50==0) {
                sem_post(&sems->parent_done);
                sem_wait(&sems->child_done);
            }
            printf("Parent: %d\n", i);
            i++;
        }
        sem_post(&sems->parent_done);
    }
    exit(EXIT_SUCCESS);
}
Run Code Online (Sandbox Code Playgroud)

Windows 对于信号量有不同的 API,请参阅MSDN 上的信号量对象

(*) edit2适合这里:在创建示例时,我注意到stdio缓冲会妨碍不睡觉。因此,也许这甚至不是您的调度程序表现不佳,而只是在何时刷新缓冲区方面stdio具有非常不可预测的行为的实现。当然这只是疯狂的猜测。你要知道的是:C中的所有句柄都是由C库的一部分缓冲的。这包括预定义的、和句柄。结果是您在输出中看到的内容不一定反映不同线程或进程创建该输出的顺序。当然,除非您像我的示例片段中那样完全禁用缓冲。FILEstdiostdinstdoutstderr