PRNG возвращает одинаковое значение во всех процессах - PullRequest
0 голосов
/ 09 ноября 2011

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

#include <sys/types.h>
#include <sys/wait.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>

#define RETURN_FAILURE_IF_TRUE(condition, ...) \
{ \
    if(condition) \
    { \
        fprintf(stderr, __VA_ARGS__); \
        return EXIT_FAILURE; \
    } \
}

#define RETURN_FAILURE_IF_FALSE(condition, ...) \
    RETURN_FAILURE_IF_TRUE(!(condition), __VA_ARGS__)

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

int nextRandomDouble(double* d)
{
    if(pthread_mutex_lock(&mutex) != 0) return 0;
    *d = drand48();
    if(pthread_mutex_unlock(&mutex) != 0) return 0;
    return 1;
}

int main()
{
    const int processes = 5;
    srand48(time(NULL));

    for(int i = 0; i < processes; ++i)
    {
        pid_t pid = fork();
        RETURN_FAILURE_IF_TRUE(pid < 0, "Fork failed.\n");
        if(pid == 0)
        {
            double d;
            RETURN_FAILURE_IF_FALSE(nextRandomDouble(&d), "PRNG failed.\n");
            printf("rnd: %f\n", d);
            return EXIT_SUCCESS;
        }
    }

    for(int i = 0; i < processes; ++i)
    {
        int status;
        pid_t pid = waitpid(-1, &status, 0);
        RETURN_FAILURE_IF_TRUE(
            (pid != 1) && (status != 0), "Child exit failed.\n"
        );
    }
    return EXIT_SUCCESS;
}

Ответы [ 3 ]

3 голосов
/ 09 ноября 2011
srand48(time(NULL));

Вы запускаете PRNG в каждом процессе с момента начала процесса до второго. Это означает, что все процессы, которые запускаются в одну и ту же секунду, инициируют PRNG с одинаковым значением.

Попробуйте:

srand48((getpid()*2654435761U)^time(NULL));
1 голос
/ 09 ноября 2011

Вы получаете одну и ту же последовательность случайных чисел в каждом процессе, потому что вы заполняете PRNG перед вызовом fork().После вызова fork() каждый процесс имеет свою собственную копию PRNG с одинаковым значением, поэтому каждый процесс получает одинаковую последовательность чисел.

Обратите внимание, чтовызовы мьютекса не нужны, потому что после fork() каждый процесс работает в своем собственном виртуальном адресном пространстве - здесь нет общего состояния между процессами.

Если вы используете pthread_create() вместо fork(),создавая отдельные потоков , потоки будут совместно использовать состояние PRNG, и каждый из них получит различное значение из последовательности PRNG:

void *thread_func(void *arg)
{
    double d;
    if (!nextRandomDouble(&d))
        fprintf(stderr, "PRNG failed.\n");
    else
        printf("rnd: %f\n", d);
    return 0;
}

int main()
{
    const int processes = 5;
    pthread_t thread[processes];
    srand48(time(NULL));

    for(int i = 0; i < processes; ++i)
    {
        int pthread_err = pthread_create(&thread[i], NULL, thread_func, NULL);
        RETURN_FAILURE_IF_TRUE(pthread_err != 0, "pthread_create failed.\n");
    }

    for(int i = 0; i < processes; ++i)
    {
        void *status;
        int pthread_err = pthread_join(thread[i], &status);
        if ((pthread_err != 0) || (status != 0))
            fprintf(stderr, "Child exit failed.\n");
    }
    return EXIT_SUCCESS;
}
0 голосов
/ 09 ноября 2011

Использование drand48 - плохая идея в многопоточной среде.Это использует общее глобальное состояние между вызовами.Поэтому либо потоки наступают друг другу на ноги (если это не делает потокобезопасным), либо бесконечно ждут своего хода (если доступ получен мьютексом, как вы это сделали).

Вместо этого используйте erand48,Это получает состояние в аргументе, который вы должны инициализировать для каждого потока другим значением.

...