2011-11-08 37 views
0

下面的代碼(以及可以編譯的結果)導致隨機數生成器返回所有進程出於某種原因的相同隨機數。那怎麼可能?我對互斥體做錯了什麼?PRNG在所有進程中返回相同的值

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

回答

1

由於您在致電fork()之前播種PRNG,因此在每個過程中您會得到相同的隨機數序列。呼叫fork()後,每個進程都有它是PRNG的自己的副本,種子具有相同的值 - 所以當然每個進程都獲得相同的數字序列。

請注意,互斥調用是不必要的,因爲在每個進程運行在其自己的虛擬地址空間之後 - 這裏的進程之間沒有共享狀態。

如果使用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

好的答案,實際上地址似乎是主要問題:提問者是混淆進程與線程。 – Quantumboredom

3
srand48(time(NULL)); 

你播種PRNG與過程開始時每個進程,到第二。這意味着在同一秒內開始的所有進程將使用相同的值對PRNG進行種子處理。

嘗試:

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

你能上指點迷津這個神奇的數字是什麼,是爲了什麼? –

+0

它來自[Knuth](http://stackoverflow.com/questions/664014/what-integer-hash-function-are-good-that-accepts-an-integer-hash-key/665545#665545)。這是一種簡單的方法來混合PID的位,以便在相差僅幾秒的時間內,僅在最後幾個位不同的PID不會抵消。 9802-> F819732A 9803-> 9650ECDB 9804-> 3488668C 9805-> D2BFE03D。注意有多少位有所不同? –

+0

聰明。與此同時,一個快速的谷歌常常變得高效。作爲參考,這是啓發性的:http://mathforum.org/kb/thread.jspa?messageID=431065&tstart=0 –

0

使用drand48是在多線程環境中一個壞主意。這是在調用之間使用共享的全局狀態。所以要麼是線程彼此踩在一起(如果這不是線程安全的),要麼等待輪到他們(如果訪問是互斥的,就像你一樣)。

改爲使用erand48。這將在參數中接收狀態,您應該爲每個線程初始化爲不同的值。

相關問題