【问题标题】:PRNG returns the same value in all processesPRNG 在所有进程中返回相同的值
【发布时间】:2011-11-08 20:01:27
【问题描述】:

以下代码(并且可以按原样编译)导致随机数生成器出于某种原因为所有进程返回完全相同的随机数。怎么可能?我对互斥锁做错了吗?

#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;
}

【问题讨论】:

    标签: c random posix mutex process


    【解决方案1】:
    srand48(time(NULL));
    

    您在每个进程中播种 PRNG,从进程开始到第二个。这意味着在同一秒开始的所有进程都以相同的值播种 PRNG。

    试试:

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

    【讨论】:

    • 你能告诉我们这个神奇的数字是什么,是为了什么?
    • 来自Knuth。这是一种混合 PID 位的简单方法,这样最后仅相差几位的 PID 不会抵消仅相差几秒钟的时间。 9802->F819732A 9803->9650ECDB 9804->3488668C 9805->D2BFE03D。注意有多少位不同?
    • 聪明。与此同时,一个快速的谷歌搜索结果证明是富有成效的。作为参考,这很有启发性:mathforum.org/kb/thread.jspa?messageID=431065&tstart=0
    • 我认为,仅使用 clock_gettime 并传递纳秒而不是秒作为种子会更好地工作...
    • @R..:假设时钟有足够的分辨率。它应该在典型的现代 PC 上具有 TSC 分辨率,这当然足够了。
    【解决方案2】:

    您在每个进程中获得相同的随机数序列,因为您在调用 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;
    }
    

    【讨论】:

    • 很好的答案,实际上解决了似乎是主要问题的问题:提问者将进程与线程混淆了。
    【解决方案3】:

    在多线程环境中使用drand48 是个坏主意。这是在调用之间使用共享的全局状态。因此,要么线程相互踩踏(如果这不是线程安全的),要么无休止地等待轮到它们(如果访问是互斥的,就像你做的那样)。

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

    【讨论】:

      猜你喜欢
      • 2013-12-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-08
      • 1970-01-01
      相关资源
      最近更新 更多