2011-11-08 9 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のコピーです。もちろん、各プロセスは同じ番号のシーケンスを取得します。

fork()の各プロセスがそれ自身の仮想アドレス空間で動作しているため、mutex呼び出しが不要であることに注意してください。ここにはプロセス間の共有状態はありません。

あなたが別のスレッドを作成し、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

[クヌース](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

Clever。その定数のための速いgoogleは、その間に生産的であることが判明した。参考までに、これは啓発されています:http://mathforum.org/kb/thread.jspa?messageID=431065&tstart=0 –

0

drand48を使用するには、マルチスレッド環境では悪い考えです。これは呼び出し間で共有されたグローバル状態を使用しています。だから、どちらかのスレッドが互いに足元を歩いているか(スレッドセーフでない場合)、無限に待っているかのように(アクセスがミューテックスされている場合)

代わりにerand48を使用してください。これは、各スレッドに対して異なる値に初期化する必要がある引数の状態を受け取ります。