1. ホーム
  2. c#

[解決済み】乱数発生器が1つの乱数しか発生させない。

2022-03-23 07:15:21

質問

以下のような関数があります。

//Function to get random number
public static int RandomNumber(int min, int max)
{
    Random random = new Random();
    return random.Next(min, max);
}

私の呼び方

byte[] mac = new byte[6];
for (int x = 0; x < 6; ++x)
    mac[x] = (byte)(Misc.RandomNumber((int)0xFFFF, (int)0xFFFFFF) % 256);

実行中にデバッガでそのループをステップすると、異なる値が得られます(これは私が望むものです)。 しかし、そのコードの2行下にブレークポイントを置くと、すべての mac 配列の値は同じです。

なぜそうなるのでしょうか?

どうすれば解決するの?

を実行するたびに new Random() はクロックを使って初期化されます。つまり、タイトなループの中では同じ値を何度も取得することになります。このような場合、1つの ランダム インスタンスを作成し 次へ を使用します。 同じ のインスタンスを作成します。

//Function to get a random number 
private static readonly Random random = new Random(); 
private static readonly object syncLock = new object(); 
public static int RandomNumber(int min, int max)
{
    lock(syncLock) { // synchronize
        return random.Next(min, max);
    }
}


Edit (コメント参照): なぜ lock ここで?

基本的には Next の内部状態を変更することになります。 Random インスタンスを作成します。それを複数のスレッドから同時に行うとなると かもしれない という議論がありますが、私たちが行っているのは 実際に は、内部実装を破壊する可能性があり、また、異なるスレッドから同じ数値を取得し始める可能性があり、これは かもしれない 問題かもしれませんし、そうでないかもしれません。しかし、内部で起こることの保証はより大きな問題です。 Random が行います。 ない は、スレッドセーフの保証を一切していません。したがって、有効なアプローチは2つあります。

  • 異なるスレッドから同時にアクセスしないように同期させる。
  • を使い分ける Random インスタンスを作成します。

どちらを使ってもかまいません。 シングル のインスタンスを複数の呼び出し元から同時に取得することは、トラブルの元でしかありません。

その lock は、これらのアプローチのうち最初の(そしてより単純な)方法を実現します。しかし、別のアプローチもあり得ます。

private static readonly ThreadLocal<Random> appRandom
     = new ThreadLocal<Random>(() => new Random());

の場合、スレッド単位になるので、同期をとる必要はありません。