2010-10-19 26 views
21

Ich versuche zu lernen, wie man rand_r verwendet, und nach dem Lesen this question bin ich immer noch ein wenig verwirrt, kann jemand bitte einen Blick und darauf hinweisen, was ich vermisse? Nach meinem Verständnis nimmt rand_r einen Zeiger auf einen Wert (oder ein Stück Speicher mit einem Anfangswert) und benutzt es, um jedes Mal, wenn es aufgerufen wird, neue Zahlen zu erzeugen. Jeder Thread, der rand_r aufruft, sollte ihm einen eindeutigen Zeiger (oder ein Stück Speicher) liefern, um "tatsächliche zufällige" Zahlen zwischen verschiedenen Threads zu erhalten. Deshalb ist dies:Wie benutze ich rand_r und wie benutze ich es threadsicher?

int globalSeed; 

//thread 1 
rand_r(&globalSeed); 

//thread 2 
rand_r(&globalSeed); 

ist die falsche Art der Verwendung. Wenn ich

int seed1,seed2; 

//thread 1 
rand_r(&seed1); 

//thread 2 
rand_r(&seed2); 

habe, wäre dies der richtige Weg, um "echte zufällige" Zahlen zwischen Threads zu generieren?


EDIT: weitere Fragen nach Antworten auf den oben genannten Teil zu lesen:

  1. wenn in Thread 1 I eine zufällige Zahl zwischen 1 bis n benötigen, soll ich (rand_r(&seed1) % (n-1)) + 1 tun? Oder gibt es andere übliche Wege, dies zu tun?
  2. Ist es richtig oder normal, wenn der Speicher für den Seed dynamisch zugewiesen wird?

Antwort

15

Das ist richtig. Was Sie im ersten Fall tun, ist die Umgehung der Thread-Sicherheitsnatur von rand_r. Bei vielen nicht threadsicheren Funktionen wird der persistente Zustand zwischen Aufrufen dieser Funktion gespeichert (z. B. der zufällige Startwert).

Mit der thread-sicheren Variante stellen Sie tatsächlich eine threadspezifische Dateneinheit (seed1 und seed2) bereit, um sicherzustellen, dass der Status nicht zwischen Threads geteilt wird.

Denken Sie daran, dass dies die Zahlen nicht wirklich zufällig macht, es macht nur die Sequenzen unabhängig voneinander. Wenn Sie sie mit demselben Seed starten, erhalten Sie wahrscheinlich die gleiche Sequenz in beiden Threads.

Nehmen wir als Beispiel, Sie erhalten eine zufällige Reihenfolge 2, 3, 5, 7, 11, 13, 17 gegeben einen Anfangswert von 0 mit einem gemeinsamen Startwert, abwechselnd Anrufe zu rand_r von zwei verschiedenen Threads verursachen diese:

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

und das ist der beste Fall - können Sie tatsächlich feststellen, dass der gemeinsame Zustand, da die Updates auf es möglicherweise nicht atomar beschädigt wird.

Bei nicht freigegebenen Zustand (mit a und b, die die zwei verschiedenen Quellen der Zufallszahlen):

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

Einige Thread-sichere Anrufe, die Sie benötigen den Thread-spezifischen Zustand so bieten, andere Sie können unter den Covern threadspezifische Daten (mit einer Thread-ID oder ähnlichen Informationen) erstellen, sodass Sie sich nie darum kümmern müssen, und Sie können denselben Quellcode in Umgebungen mit und ohne Threads verwenden. Ich bevorzuge das letztere selbst, einfach weil es mir das Leben leichter macht.


Zusatzmaterial für editierte Frage:

> 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?

Angenommen, Sie einen Wert zwischen 1 und ninklusive wollen (rand_r(&seed1) % n) + 1 verwenden. Das erste Bit gibt Ihnen einen Wert von 0 bis einschließlich n-1, dann fügen Sie 1 hinzu, um den gewünschten Bereich zu erhalten.

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

Der Samen hat hartnäckig so lange sein, wie Sie es verwenden sind. Sie könnten es dynamisch im Thread zuweisen, aber Sie könnten es auch in der Top-Level-Funktion des Threads deklarieren. In beiden Fällen müssen Sie die Adresse irgendwie an die unteren Ebenen weiterleiten (es sei denn, Ihr Thread ist nur diese eine Funktion, die unwahrscheinlich ist).

Sie könnten es entweder durch die Funktionsaufrufe weiterleiten oder irgendwie ein globales Array einrichten, wo die unteren Ebenen die richtige Startadresse finden können.

Alternativ können Sie, da Sie sowieso ein globales Array benötigen, ein globales Array mit Seeds anstelle von Seed-Adressen verwenden, die die unteren Ebenen zum Ermitteln ihres Seeds verwenden könnten.

Sie hätten wahrscheinlich (in beiden Fällen der Verwendung des globalen Arrays) eine verschlüsselte Struktur, die die Thread-ID als Schlüssel und den zu verwendenden Seed enthält. Sie müssten dann Ihre eigenerand() Routine schreiben, die den richtigen Samen lokalisiert und rand_r() damit genannt hat.

Diese ist warum ich Bibliothek Routinen bevorzugen, die dies unter der Decke mit Thread-spezifischen Daten tun.

+0

danke !! Der beste Weg ist also immer unterschiedliche Samen mit unterschiedlichen Anfangswerten für verschiedene Fäden zu verwenden, oder? – derrdji

+0

würde ich normalerweise sagen, ja. Aber es gibt Situationen, in denen Sie den Status gemeinsam haben könnten (erstes Diagramm in meiner Antwort). In diesem Fall könnten Sie die Möglichkeit der Korruption vermeiden, indem Sie 'rand_r' mit dem Status _same_ aufrufen, aber durch einen Mutex geschützt werden. Das einzige, was Sie nicht tun wollen, ist, beide Sequenzen mit dem gleichen Seed zu initialisieren, da die Sequenzen dann identisch wären. – paxdiablo

+0

ja, danke !! Ich wollte gerade fragen, ob ich einen freigeben und den rand_r-Aufruf mit einem Mutex-Lock überwachen könnte. – derrdji