Utiliser rand () de stdlib à partir de plusieurs threads

J’ai plusieurs threads qui exécutent tous la même fonction. Dans chacun d’eux, ils génèrent un nombre aléatoire différent plusieurs fois. Nous avons essayé de le faire en plaçant srand(time(0)) au début de la fonction, mais il semble qu’ils obtiennent tous le même numéro.

Faut-il appeler srand(time(0)) une seule fois par programme, c’est-à-dire au début de main (par exemple), au début de chaque fonction appelée plusieurs fois, ou autre chose?

srand () génère le générateur de nombres aléatoires. Vous ne devriez avoir à appeler srand(time(NULL)) qu’une seule fois au démarrage.

Cela dit, la documentation indique:

La fonction rand() n’est pas réentrante ou thread-safe , car elle utilise un état caché modifié à chaque appel. Cela pourrait être simplement la valeur de départ à utiliser lors du prochain appel, ou cela pourrait être quelque chose de plus élaboré. Pour obtenir un comportement reproductible dans une application threadée, cet état doit être explicite. La fonction rand_r() est fournie avec un pointeur sur un unsigned int , à utiliser comme état. C’est une très petite quantité d’état, donc cette fonction sera un générateur pseudo-aléatoire faible. Essayez drand48_r (3).

La partie soulignée de ce qui précède est probablement la raison pour laquelle tous vos fils obtiennent le même numéro.

Si vous lancez tous les threads en même temps, le temps envoyé à srand est probablement le même pour chaque thread. Comme ils ont tous la même graine, ils renvoient tous la même séquence. Essayez d’utiliser autre chose comme une adresse mémoire d’une variable locale.

De la page de manuel rand :

La fonction rand () n’est pas réentrante ou thread-safe, car elle utilise un état caché modifié à chaque appel.

Donc, ne l’utilisez pas avec du code threadé. Utilisez rand_r (ou drand48_r si vous êtes sous linux / glibc). Saisissez chaque RNG avec une valeur différente (vous pouvez créer un premier RNG dans le thread principal pour produire des graines aléatoires pour celles de chaque thread).

Comme vous utilisez C ++, plutôt que C, vous pouvez éviter les problèmes de thread souvent associés à srand / rand en utilisant c ++ 11. Cela dépend de l’utilisation d’un compilateur récent qui supporte ces fonctionnalités. Vous utiliseriez un moteur et une dissortingbution séparés sur chaque thread. L’exemple agit comme un dé.

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

J’ai renvoyé à Wikipedia C ++ 11 et Boost au hasard pour répondre à cette question.

C’est une bonne question. Je ne peux pas y répondre directement parce que je pense qu’il y a des problèmes plus importants. De toute façon, il ne semble même pas évident que rand soit sûr. Il maintient l’état interne et il ne semble pas être bien défini si c’est par processus ou par thread, et si c’est par processus si c’est thread-safe.

Pour être sûr que je verrouillerais un mutex autour de chaque access.

Ou de préférence utiliser un générateur mieux défini tel que celui de boost

C n’a pas été conçu pour le multithreading, donc le comportement de srand () avec multithreading n’est pas défini et dépend de la bibliothèque d’exécution C.

De nombreuses bibliothèques d’exécution Unix / Linux C utilisent un seul état statique, auquel l’access n’est pas sûr à partir de plusieurs threads. Avec ces temps d’exécution C, vous ne pouvez pas utiliser srand () et rand () de plusieurs threads. Les autres temps d’exécution Unix C peuvent se comporter différemment.

Le moteur d’exécution Visual C ++ utilise l’état interne par thread, il est donc sûr d’appeler srand () pour chaque thread. Mais comme Neil l’a fait remarquer, vous allez probablement semer tous les threads avec la même valeur – donc graver avec (time + thread-id) à la place.

Bien sûr, pour la portabilité, utilisez des objects aléatoires plutôt que la fonction rand, et vous ne dépendez pas du tout de l’état caché. Vous avez toujours besoin d’un object par thread et semer chaque object avec (time + thread-id) est toujours une bonne idée.

Ils obtiennent tous le même numéro parce que vous commencez probablement tous les threads en même temps, ou ils utilisent tous la même graine statique, auquel cas vous êtes un peu bourré. Vous avez besoin d’une meilleure source d’entropie que de temps (). Cependant, un rapide hack serait de générer avec (time * thread-id) où id-thread est l’id de chaque thread de travail.

Bien sûr, la solution correcte en C ++ est de ne pas utiliser des fonctions de générateur de nombres aléatoires, mais d’utiliser des objects générateurs de nombres aléatoires, tels que ceux fournis par la bibliothèque Boost random number. -sûr. Voir cette réponse que j’ai préparée plus tôt pour un exemple. Cependant, il peut toujours y avoir un problème à fournir une entropie suffisante dans un programme MT, car l’utilisation de time () aura toujours le problème mentionné ci-dessus.