web-dev-qa-db-ja.com

Rand_rを使用するにはどうすればよいですか?また、スレッドセーフな方法で使用するにはどうすればよいですか?

Rand_rの使い方を学ぼうとしていますが、読んだ後 この質問 まだ少し混乱しています。誰かが私が欠けているものを見て指摘してもらえますか?私の理解では、Rand_rはある値(またはある初期値を持つメモリの一部)へのポインタを取り、それを使用して、呼び出されるたびに新しい数値を生成します。 Rand_rを呼び出す各スレッドは、異なるスレッド間で「実際の乱数」を取得するために、一意のポインター(またはメモリの一部)を提供する必要があります。これが理由です:

int globalSeed;

//thread 1
Rand_r(&globalSeed);

//thread 2
Rand_r(&globalSeed);

それを使用する間違った方法です。私が持っている場合

int seed1,seed2;

//thread 1
Rand_r(&seed1);

//thread 2
Rand_r(&seed2);

これは、スレッド間で「真の乱数」を生成する正しい方法でしょうか?


編集:上記の部分への回答を読んだ後の追加の質問:

  1. スレッド1で1からnまでの乱数が必要な場合、(Rand_r(&seed1) % (n-1)) + 1を実行する必要がありますか?または、これを行う他の一般的な方法はありますか?
  2. シードのメモリが動的に割り当てられるのは正しいですか、それとも正常ですか?
21
derrdji

そのとおりです。最初のケースで行っているのは、_Rand_r_のスレッドセーフの性質をバイパスすることです。多くの非スレッドセーフ関数では、永続的な状態がその関数の呼び出しの間に保存されます(ここではランダムシードなど)。

スレッドセーフバリアントでは、実際にスレッド固有のデータ(_seed1_および_seed2_)を提供して、状態がスレッド間で共有されないようにします。

これは数字を真にランダムにするのではなく、シーケンスを互いに独立させるだけであることに注意してください。同じシードで開始すると、おそらく両方のスレッドで同じシーケンスが得られます。

例として、初期シードが0の場合、ランダムシーケンス2、3、5、7、11、13、17を取得するとします。共有シードを使用すると、2つの異なるスレッドから_Rand_r_を交互に呼び出すことができます。これを引き起こす:

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

それがbestの場合です-共有状態の更新がアトミックではない可能性があるため、実際には共有状態が破損していることに気付く場合があります。

非共有状態の場合(abは乱数の2つの異なるソースを表します):

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

一部のスレッドセーフ呼び出しでは、このようなスレッド固有の状態を指定する必要があります。その他の呼び出しでは、(スレッドIDまたは同様の情報を使用して)内部でスレッド固有のデータを作成できるため、心配する必要はありません。スレッド環境と非スレッド環境でまったく同じソースコード。私は後者の方が好きです。なぜなら、それが私の人生を楽にしてくれるからです。


編集された質問のための追加のもの:

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

_1_とn包括的の間の値が必要な場合は、_(Rand_r(&seed1) % n) + 1_を使用します。最初のビットは_0_から_n-1_までの値を提供し、次に1を加算して目的の範囲を取得します。

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

シードは、使用している限り永続的である必要があります。スレッドで動的に割り当てることもできますが、スレッドの最上位関数で宣言することもできます。どちらの場合も、何らかの方法でアドレスを下位レベルに伝達する必要があります(スレッドがその1つの関数である可能性が低い場合を除きます)。

関数呼び出しを介してそれを渡すか、下位レベルが正しいシードアドレスを検出できるように何らかの方法でグローバル配列を設定することができます。

あるいは、とにかくグローバル配列が必要なので、シードアドレスではなくシードのグローバル配列を使用できます。これは、下位レベルがシードを検出するために使用できます。

おそらく(グローバル配列を使用する場合の両方で)、キーとしてのスレッドIDと使用するシードを含むキー構造があります。次に、正しいシードを見つけてRand()を呼び出すownRand_r()ルーチンを作成する必要があります。

Thisこれが、スレッド固有のデータを隠蔽してこれを行うライブラリルーチンを好む理由です。

16
paxdiablo