Как сделать порядок вывода случайным при синхронизации процесса, семафор? - PullRequest
0 голосов
/ 15 декабря 2018
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; 
}

Я пытаюсь синхронизировать процесс, он работает отлично, но дочерний процесс начинает записывать текст Bonjour monde после завершения родительского процесса (после 10 Hello Worlds).Может ли порядок вывода быть случайным, как

Hello World
Bonjour monde
Bonjour monde
Hello World
....

Вместо 10 Hello Worlds и затем 10 Bonjour mondes.

Ответы [ 3 ]

0 голосов
/ 15 декабря 2018

так как вы делаете 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

на самом деле, чтобы получить 10 "привет" сообщений после 10 "Бонжор" сообщений, тогда мы должны попытаться вызвать sem_wait() sem_post() дои после завершения for() в дочернем и родительском элементах, а также мы не можем гарантировать, какой из них будет запланирован первым (родитель или дочерний элемент могут выполняться первым), то есть

if (fork()) {
    sem_wait(sem);
    for (i = 0; i < 10; i++) {
        display("Hello world\n");
    }
    sem_post(sem);
    wait(NULL);
}
0 голосов
/ 16 декабря 2018

Похоже, у вас более сильные ожидания в отношении «случайного» результата, чем означает сам термин.Ваш исходный код действительно недетерминирован ( то есть случайный) в отношении порядка вывода.Это может быть яснее в долгосрочной перспективе.Случается, что это не одобряет переключение с одного выхода на другой, поэтому выход слипается, но это не делает его неслучайным.

Поскольку вы также недовольны детерминированным чередованием, я полагаю, что вы что-то имеете в видудля каждой строки имеется равный шанс для каждого выхода, не связанный с предыдущей строкой.В этом случае вам нужно, по иронии судьбы, проявить некоторый контроль.Например, вы можете адаптировать решение с двумя семафорами @ CraigEstey, чтобы вместо каждого процесса, детерминистически проводящего отправку на семафор другого, каждый случайным образом выбирал, какой семафор для публикации, с помощью генератора псевдослучайных чисел, такого как rand().Я оставляю детали в качестве упражнения.

0 голосов
/ 15 декабря 2018

У вас есть состояние гонки.

Когда основной процесс выполняет sem_post, он зацикливается [быстро] и [почти] немедленно выполняет следующий 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;
}
...