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

C 即使在父进程中使用sleep()时,我的子进程也是最后执行的,c,process,parallel-processing,fork,sleep,C,Process,Parallel Processing,Fork,Sleep,有人能解释为什么父进程总是在子进程中的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(); i

有人能解释为什么父进程总是在子进程中的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);
}
直到父项完成,子项也一样

原因可能是我在单核CPU上测试吗?如果我有多核设置,结果会改变吗?睡眠(50)确实在工作——因为脚本完成需要很长时间——为什么CPU不切换进程?是否存在这样的情况,例如在while循环中,进程对CPU拥有“更多”或独占权限

谢谢你的帮助。:)

是否存在这样的情况,例如在while循环中,进程对CPU拥有“更多”或独占权限

嗯,这还没有定义,但那太疯狂了

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

我得说你的调度程序表现得很奇怪,可能只是坏了。尽管如此,依赖调度器的任何特定行为从来都不是一个好主意。总是假设它是坏的和疯狂的——如果你的代码允许一个特定的执行序列,将会有一个调度程序疯狂地选择它。(*)

因此,当您需要依赖某些同步时,可以使用同步原语(
信号量
互斥体
例如具有用于不同进程的共享版本——您也可以在某些场景中使用
管道

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

首先是使用
管道的版本(ab):

#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);
}
#包括
#包括
#包括
#包括
int main(int argc,字符**argv){
int i=10000;
int parent_done[2];
int child_done[2];
字符伪[1]={0};
int-pid;
管道(母管已完成);
管道(未完成);
/*stdio缓冲将导致混合输出*/
setvbuf(标准输出,0,_IONBF,0);
pid=fork();
如果(pid==0){
关闭(父项完成[1]);
关闭(child_done[0]);
而(i>50){
如果(i%100==0){
如果(i<10000)写入(child_done[1],dummy,1);
读取(父项完成[0],伪项,1);
}
printf(“子项:%d\n”,i);
我--;
}
}否则{
关闭(父项_完成[0]);
关闭(child_done[1]);
而(i<15000){
如果(i%50==0){
写入(父项完成[1],伪项,1);
读取(child_done[0],dummy,1);
}
printf(“父项:%d\n”,i);
i++;
}
}
退出(退出成功);
}
然后,使用POSIX信号量也是一样(因为信号量用于同步,所以更干净):

#包括
#包括
#包括
#包括
#包括
#包括
#包括
结构信号量
{
完成扫描;
扫描完成;
};
int main(int argc,字符**argv){
int i=10000;
int-pid;
/*映射信号量的共享内存*/
结构信号量*sems=mmap(0,sizeof(*sems),PROT_READ | PROT_WRITE,
MAP|u SHARED | MAP|u ANONYMOUS,-1,0);
/*将两个信号量初始化为“共享”并具有初始计数
*0的*/
sem_init(&sems->parent_done,1,0);
sem_init(&sems->child_done,1,0);
/*stdio缓冲将导致混合输出*/
setvbuf(标准输出,0,_IONBF,0);
pid=fork();
如果(pid==0){
而(i>50){
如果(i%100==0){
如果(i<10000)sem\u post(&sems->child\u done);
sem_wait(&sems->parent_done);
}
printf(“子项:%d\n”,i);
我--;
}
sem_post(&sems->child_done);
}否则{
而(i<15000){
如果(i%50==0){
sem_post(&sems->家长_完成);
sem_wait(&sems->child_done);
}
printf(“父项:%d\n”,i);
i++;
}
sem_post(&sems->家长_完成);
}
退出(退出成功);
}
Windows有不同的信号量API,请参阅


(*)edit2适用于此:在创建示例时,我注意到
stdio
缓冲在不睡觉的情况下会遇到阻碍。因此,可能不是您的调度程序表现不好,而是
stdio
的一个实现,在何时刷新缓冲区方面具有非常不可预测的行为。当然,这只是胡乱猜测。您必须知道的是:C中的所有
文件
句柄都由C库的
stdio
部分缓冲。这包括预定义的
stdin
stdout
stderr
句柄。结果是,您在输出中看到的并不一定反映不同线程或进程创建该输出的顺序。当然,除非像我的示例代码片段中那样完全禁用缓冲。

如果有人想复制/粘贴代码,请将sleep(50)更改为sleep(30)。睡眠对我来说太长了。我马上就要编辑了。@chh我添加了一些示例代码。。。但不是windows,现在太懒了(虽然使用管道的版本可能会工作,但现在还不确定
msvcrt
是否提供了标准的
pipe()
调用)@chh,现在添加了另一个可能导致您第一次体验到的反直觉行为的原因。这正是我要寻找的。谢谢你花时间。哈哈,我在OP代码上找了很久,没有发现有什么大问题。我也不知道为什么它会表现得很“奇怪”:(@MartinJames)当我第一次看到代码时,可能的
stdio缓冲的“gotcha”对我来说也不明显,我只看到它发生在
#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);
}
#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);
}