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);
これは、スレッド間で「真の乱数」を生成する正しい方法でしょうか?
編集:上記の部分への回答を読んだ後の追加の質問:
(Rand_r(&seed1) % (n-1)) + 1
を実行する必要がありますか?または、これを行う他の一般的な方法はありますか?そのとおりです。最初のケースで行っているのは、_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の場合です-共有状態の更新がアトミックではない可能性があるため、実際には共有状態が破損していることに気付く場合があります。
非共有状態の場合(a
とb
は乱数の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これが、スレッド固有のデータを隠蔽してこれを行うライブラリルーチンを好む理由です。