PRNG在所有进程中返回相同的值

时间:2011-11-08 20:01:28

标签: c random posix mutex process

下面的代码(并且可以按原样编译)导致随机数生成器由于某种原因为所有进程返回相同的随机数。怎么会这样?我是否在使用互斥锁做错了什么?

#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 个答案:

答案 0 :(得分:3)

srand48(time(NULL));

您可以在流程开始时将每个流程中的PRNG播种到第二个流程。这意味着在相同的第二个种子中开始的所有进程都使PRNG具有相同的值。

尝试:

srand48((getpid()*2654435761U)^time(NULL));

答案 1 :(得分:1)

您在每个进程中获得相同的随机数序列,因为您在调用fork()之前为PRNG播种。在调用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;
}

答案 2 :(得分:0)

在多线程环境中使用drand48是一个坏主意。这是在调用之间使用共享的全局状态。因此,要么线程踩在彼此的脚上(如果这不是线程安全的话),要么轮流等待(如果访问是互斥的,就像你做的那样)。

请改用erand48。这会在参数中接收您应该为每个线程初始化为不同值的状态。