如何在进程同步,信号量中使输出顺序随机?

时间:2018-12-15 19:36:07

标签: c synchronization semaphore

int main() {
    sem_t * sem;

    sem = sem_open("share", O_CREAT, 0 ,1);
    sem_unlink("share");

    int i;
    if (fork()) {      
        for (i=0;i<10;i++) {
            sem_wait(sem);
            display("Hello world\n");
            sem_post(sem);
        }
        wait(NULL);
    } else {
        for (i=0;i<10;i++) {
            sem_wait(sem);
            display("Bonjour monde\n");
            sem_post(sem);
        }

    }   
    return 0; 
}

我正在尝试在进程之间进行同步,它可以正常运行,但是子进程在父进程完成(在10个Hello世界之后)之后开始记录Bonjour monde文本。输出顺序可以像

一样是随机的吗?
Hello World
Bonjour monde
Bonjour monde
Hello World
....

而不是10个Hello World,然后是10个Bonjour世界。

3 个答案:

答案 0 :(得分:0)

您似乎对“随机”输出的期望比其表面上的含义更强。就输出顺序而言,您的原始代码确实是不确定的(随机)。从长远来看,这可能会更清楚。碰巧不利于从一个输出切换到另一个输出,因此输出会成簇,但这并不会使其成为非随机的。

由于您也对确定性变更不满意,因此我想您的意思是,每行输出与上一行无关的机会均等。在这种情况下,具有讽刺意味的是,您需要施加一些控制权。例如,您可以改编@CraigEstey的两个信号量解决方案,以使每个过程都可以通过伪随机数生成器(例如rand())来随机选择要发布到哪个信号量,而不是确定地将每个信号量发布到另一个信号量。我把细节留作练习。

答案 1 :(得分:-1)

您有比赛条件。

当主进程执行sem_post时,它会[快速]循环,[几乎]立即执行 next sem_wait并在子进程有机会之前获取信号量得到它。

简单/幼稚的解决方案是在每个循环的底部添加一个小的usleep

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <semaphore.h>

int
main()
{
    sem_t *sem;

    setlinebuf(stdout);

    sem = sem_open("share", O_CREAT, 0, 1);
    sem_unlink("share");

    int i;

    if (fork()) {
        for (i = 0; i < 10; i++) {
            sem_wait(sem);
            printf("Hello world\n");
            sem_post(sem);
            usleep(10);
        }
        wait(NULL);
    }
    else {
        for (i = 0; i < 10; i++) {
            sem_wait(sem);
            printf("Bonjour monde\n");
            sem_post(sem);
            usleep(10);
        }

    }
    return 0;
}

现在输出为:

Hello world
Bonjour monde
Hello world
Bonjour monde
Hello world
Bonjour monde
Hello world
Bonjour monde
Hello world
Bonjour monde
Hello world
Bonjour monde
Hello world
Bonjour monde
Hello world
Bonjour monde
Hello world
Bonjour monde
Hello world
Bonjour monde

更新:

如下所述,最好使用 two 信号量解决方案,因为它可以保证交替发生:

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <semaphore.h>

int
main()
{
    sem_t *sempar;
    sem_t *semcld;

    setlinebuf(stdout);

    sem_unlink("parent");
    sempar = sem_open("parent", O_CREAT, 0, 1);

    // by blocking child semaphore, this guarantees parent starts first
    sem_unlink("child");
    semcld = sem_open("child", O_CREAT, 0, 0);

    int i;

    if (fork()) {
        for (i = 0; i < 10; i++) {
            sem_wait(sempar);
            printf("Hello world (%d)\n",i);
            sem_post(semcld);
        }
        wait(NULL);
    }

    else {
        for (i = 0; i < 10; i++) {
            sem_wait(semcld);
            printf("Bonjour monde (%d)\n",i);
            sem_post(sempar);
        }
    }

    return 0;
}

更新#2:

  

但是输出不是随机的,它将打印HelloWorld,然后打印Bonjour并重复。

好的,那么单个信号量解决方案是更好的选择,但需要进行一些修改:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <time.h>
#include <sys/wait.h>
#include <semaphore.h>

sem_t *sem;

void
doloop(time_t seed,const char *msg)
{
    int i;
    int rval;

    srand(seed);

    for (i = 0; i < 30; i++) {
        rval = rand() % 100000;
        sem_wait(sem);
        printf("%s i=%d rval=%d\n",msg,i,rval);
        sem_post(sem);
        usleep(rval);
    }
}

int
main()
{
    time_t seed;

    setlinebuf(stdout);

    sem_unlink("share");
    sem = sem_open("share", O_CREAT, 0, 1);

    seed = time(NULL);

    if (fork()) {
        doloop(seed,"Hello World");
        wait(NULL);
    }

    else {
        doloop(seed ^ ~0,"Bonjour monde");
    }

    return 0;
}

答案 2 :(得分:-1)

在每次迭代中都执行sem_post()时,它将唤醒每次迭代中等待sem的所有进程,我们无法保证在sem_post()之后相同的进程将其取回,所以

发生这种情况的主要原因是计算机的调度行为。 您的代码在我的linux机器中产生以下输出

Hello world Bonjour monde Bonjour monde Bonjour monde Bonjour monde Bonjour monde Bonjour monde Bonjour monde Bonjour monde Bonjour monde Hello world Hello world Hello world Hello world Bonjour monde Hello world Hello world Hello world Hello world Hello world

实际上是要让guanrantte在10条“ Bonjor”消息之后获得10条“ hello”消息,然后我们必须尝试在孩子和父母中完成sem_wait() sem_post()之前和之后调用for(),而且我们不能guarntee,哪个将最先安排(父母或孩子可以先运行),即

if (fork()) {
    sem_wait(sem);
    for (i = 0; i < 10; i++) {
        display("Hello world\n");
    }
    sem_post(sem);
    wait(NULL);
}
相关问题