Utilizzo di stdlib’s rand () da più thread

Ho diversi thread che hanno tutti la stessa funzione. In ognuno di questi generano un numero casuale diverso più volte. Abbiamo provato a farlo inserendo srand(time(0)) all’inizio della funzione, ma sembra che tutti abbiano lo stesso numero.

Dobbiamo chiamare srand(time(0)) solo una volta per programma, cioè all’inizio del main (ad esempio), all’inizio di ogni funzione che viene chiamata più volte o qualcos’altro?

srand () semina il generatore di numeri casuali. Dovresti solo chiamare srand(time(NULL)) una volta durante l’avvio.

Detto questo, la documentazione afferma:

La funzione rand() non è rientrante o thread-safe , poiché utilizza lo stato nascosto che viene modificato in ogni chiamata. Questo potrebbe essere solo il valore iniziale da utilizzare per la prossima chiamata, o potrebbe essere qualcosa di più elaborato. Per ottenere un comportamento riproducibile in un’applicazione con thread, questo stato deve essere reso esplicito. La funzione rand_r() viene fornita con un puntatore a un unsigned int , da utilizzare come stato. Questa è una quantità molto piccola di stato, quindi questa funzione sarà un debole generatore pseudo-casuale. Prova invece drand48_r (3).

La parte enfatizzata di quanto sopra è probabilmente la ragione per cui tutti i tuoi thread ottengono lo stesso numero.

Se si stanno avviando i thread tutti allo stesso tempo, il tempo inviato a srand è probabilmente lo stesso per ogni thread. Poiché hanno tutti lo stesso seme, tutti restituiscono la stessa sequenza. Prova a usare qualcos’altro come un indirizzo di memoria da una variabile locale.

Dalla pagina man di rand :

La funzione rand () non è rientrante o thread-safe, poiché utilizza lo stato nascosto che viene modificato in ogni chiamata.

Quindi non usarlo con il codice filettato. Usa rand_r (o drand48_r se sei su linux / glibc). Seme ogni RNG con un valore diverso (potresti seminare un primo RNG nel thread principale per produrre semi casuali per quelli in ogni thread).

Dato che stai usando C ++, piuttosto che C, potresti essere in grado di evitare i problemi di threading spesso associati a srand / rand usando c ++ 11. Questo dipende dall’utilizzo di un compilatore recente che supporta queste funzionalità. Dovresti utilizzare un motore e una distribuzione separati su ogni thread. L’esempio si comporta come un dado.

 #include  #include  std::uniform_int_distribution dice_distribution(1, 6); std::mt19937 random_number_engine; // pseudorandom number generator auto dice_roller = std::bind(dice_distribution, random_number_engine); int random_roll = dice_roller(); // Generate one of the integers 1,2,3,4,5,6. 

Mi sono riferito a Wikipedia C ++ 11 e Boost random quando ho risposto a questa domanda.

Questa è una bella domanda. Non posso rispondere direttamente perché penso ci siano problemi più grandi. Non sembra nemmeno chiaro che Rand sia comunque sicuro. Mantiene lo stato interno e non sembra essere ben definito se questo è per processo o per thread e se è per processo se è thread-safe.

Per essere sicuro avrei bloccato un mutex attorno ad ogni accesso.

O preferibilmente utilizzare una generazione meglio definita come quella di boost

C non è stato progettato per il multithreading, quindi il comportamento di srand () con il multithreading non è definito e dipende dalla libreria di runtime C.

Molte librerie di runtime Unix / Linux C utilizzano un singolo stato statico, che non è sicuro accedere da più thread, quindi con queste C runtime non è ansible usare srand () e rand () da più thread. Altri runtime di Unix C possono comportarsi diversamente.

Il runtime di Visual C ++ utilizza lo stato interno per thread, quindi è sicuro chiamare srand () per ogni thread. Ma come ha fatto notare Neil, probabilmente seminerai tutti i thread con lo stesso valore – quindi semina con (time + thread-id).

Ovviamente, per la portabilità, usa oggetti Random piuttosto che rand, e quindi non dipenderesti affatto dallo stato nascosto. Hai ancora bisogno di un object per thread e seminare ogni object con (time + thread-id) è ancora una buona idea.

Ricevono tutti lo stesso numero perché presumibilmente iniziano tutti i thread allo stesso tempo, oppure usano tutti lo stesso seme statico, nel qual caso sei un po ‘imbottito. Hai bisogno di una migliore fonte di entropia rispetto al tempo (). Tuttavia, un attacco rapido potrebbe essere l’inizializzazione con (time * id-thread) dove thread-id è l’id di ciascun thread di lavoro.

Ovviamente, la soluzione corretta in C ++ non è quella di utilizzare funzioni di generatore di numeri casuali, ma di utilizzare oggetti generatori di numeri casuali, come quelli forniti dalla libreria di numeri casuali Boost, che per loro stessa natura (perché sono basati sullo stack) sono thread -sicuro. Vedi questa risposta che ho preparato in precedenza per un esempio. Tuttavia, potrebbe esserci ancora un problema nel fornire una sufficiente entropia in un programma MT, poiché l’uso di time () avrà ancora il problema che ho menzionato sopra.