2010-10-19 7 views
21

J'essaie d'apprendre comment utiliser rand_r, et après avoir lu this question je suis encore un peu confus, quelqu'un peut-il s'il vous plaît jeter un oeil et de signaler ce que je suis manquant? À mon sens, rand_r prend un pointeur vers une valeur (ou un morceau de mémoire avec une valeur initiale) et l'utilise pour générer de nouveaux nombres chaque fois qu'il est appelé. Chaque thread qui appelle rand_r doit lui fournir un pointeur unique (ou un morceau de mémoire) pour obtenir des nombres "aléatoires" entre différents threads. C'est pourquoi:Comment utiliser rand_r et comment l'utiliser en toute sécurité?

int globalSeed; 

//thread 1 
rand_r(&globalSeed); 

//thread 2 
rand_r(&globalSeed); 

est la mauvaise façon de l'utiliser. Si j'ai

int seed1,seed2; 

//thread 1 
rand_r(&seed1); 

//thread 2 
rand_r(&seed2); 

ce serait la bonne façon de générer des nombres "vrais aléatoires" entre les threads?


EDIT: questions supplémentaires après avoir lu les réponses à la partie ci-dessus:

  1. si fil 1 je besoin d'un nombre aléatoire entre 1 à n, dois-je faire (rand_r(&seed1) % (n-1)) + 1? Ou il y a une autre façon commune de faire cela?
  2. Est-ce normal ou normal si la mémoire pour la graine est allouée dynamiquement?

Répondre

15

C'est correct. Ce que vous faites dans le premier cas est en contournant la nature de thread-sécurité de rand_r. Avec de nombreuses fonctions non thread-safe, l'état persistant est stocké entre les appels à cette fonction (comme la graine aléatoire ici).

Avec la variante thread-safe, vous fournissez réellement une donnée spécifique au thread (seed1 et seed2) pour vous assurer que l'état n'est pas partagé entre les threads. Gardez à l'esprit que cela ne rend pas les nombres vraiment aléatoires, cela rend les séquences indépendantes l'une de l'autre. Si vous les lancez avec la même graine, vous obtiendrez probablement la même séquence dans les deux threads. A titre d'exemple, disons que vous obtenez une séquence aléatoire 2, 3, 5, 7, 11, 13, 17 avec une graine initiale de 0. Avec une graine partagée, l'alternance des appels à rand_r à partir de deux threads différents faire ceci:

thread 1    thread 2 
      <--- 2 
       3 ---> 
      <--- 5 
       7 ---> 
      <--- 11 
       13 ---> 
      <--- 17 

et c'est le meilleur cas - vous pouvez effectivement constater que l'état partagé est corrompu depuis les mises à jour à ce sujet ne peut pas être atomique.

Avec état non partagé (avec a et b représentant les deux différentes sources de nombres aléatoires):

thread 1    thread 2 
      <--- 2a 
       2b ---> 
      <--- 3a 
       3b ---> 
      <--- 5a 
       5b ---> 
       :: 

Certains appels thread-safe vous demander de fournir l'état thread spécifique comme celui-ci, d'autres peut créer des données spécifiques aux threads sous les couvertures (en utilisant un ID de thread ou des informations similaires) afin de ne jamais avoir à vous en soucier, et vous pouvez utiliser exactement le même code source dans des environnements threaded et non threaded. Je préfère ce dernier moi-même, simplement parce que cela me facilite la vie.


choses supplémentaires pour la question éditée:

> If in thread 1, I need a random number between 1 to n, should I do '(rand_r(&seed1) % (n-1)) + 1', or there is other common way of doing this?

En supposant que vous voulez une valeur entre 1 et n inclus, utilisez (rand_r(&seed1) % n) + 1. Le premier bit vous donne une valeur comprise entre 0 et n-1 inclusivement, puis vous en ajoutez 1 pour obtenir la plage souhaitée.

> Is it right or normal if the memory for the seed is dynamically allocated?

La graine doit être persistante aussi longtemps que vous l'utilisez. Vous pouvez l'allouer dynamiquement dans le thread mais vous pouvez également le déclarer dans la fonction de premier niveau du thread. Dans les deux cas, vous devrez communiquer l'adresse aux niveaux inférieurs d'une façon ou d'une autre (sauf si votre thread est juste une fonction qui est peu probable).

Vous pouvez soit le passer à travers les appels de fonction ou configurer un tableau global en quelque sorte où les niveaux inférieurs peuvent découvrir l'adresse de départ correcte. De même, puisque vous avez besoin d'un tableau global, vous pouvez avoir un tableau global de graines plutôt que des adresses de départ, que les niveaux inférieurs pourraient utiliser pour découvrir leur graine.

Vous auriez probablement (dans les deux cas d'utiliser le tableau global) une structure à clé contenant l'ID de thread comme une clé et la graine à utiliser. Vous devrez alors écrire votre routine rand() qui a localisé la bonne graine et qui s'appelle rand_r() avec cela.

Cette est pourquoi je préfère les routines de bibliothèque qui le font sous les couvertures avec des données spécifiques au fil.

+0

merci !! donc la meilleure façon est toujours d'utiliser des graines différentes avec des valeurs initiales différentes pour différents threads, non? – derrdji

+0

Je dirais habituellement, oui. Mais il y a des situations où vous pourriez _want_ shared state (premier diagramme dans ma réponse), auquel cas vous pourriez éviter la possibilité de corruption en appelant 'rand_r' avec l'état _same_ mais protégé par un mutex. La seule chose que vous ne voulez pas faire est d'initialiser les deux séquences avec la même graine puisque les séquences seraient alors identiques. – paxdiablo

+0

oui, merci !! J'étais sur le point de demander si je pouvais en partager un et garder l'appel de rand_r avec un verrou de mutex. – derrdji

Questions connexes