2016-10-30 14 views
2

私は、並列ランダムジェネレータにThreadLocalを使用しようとしています。私のテストコードは、次のようなものです:ThreadLocalを並列ループの間に置くことは良い/悪い考えですか?

class Program 
{ 
    static void Main() 
    { 
     using (MyClass myClass = new MyClass()) 
     { 
      for (int i = 0; i < 3; i++) 
      { 
       Console.WriteLine("\nPass {0}: ", i + 1); 
       myClass.Execute(); 
      } 

      Console.WriteLine("\nRandom Generators used: {0}", myClass.ListRNG.Count); 
     } 

     Console.WriteLine("\nPress any key..."); 
     Console.ReadKey(); 
    } 
} 

sealed class MyClass : IDisposable 
{ 
    ThreadLocal<RandomGenerator> threadRNG = new ThreadLocal<RandomGenerator>(() => 
                new RandomGenerator(), true); 

    public IList<RandomGenerator> ListRNG { get { return threadRNG.Values; } } 

    public void Execute() 
    { 
     Action<int> action = (i) => 
     { 
      bool repeat = threadRNG.IsValueCreated; 
      List<int> ints = new List<int>(); 
      int length = threadRNG.Value.Next(10); 
      for (int j = 0; j < length; j++) 
       ints.Add(threadRNG.Value.NextInt(100)); 
      Console.WriteLine("Action {0}. ThreadId {1}{2}. Randoms({3}): {4}", 
       i + 1, Thread.CurrentThread.ManagedThreadId, 
       repeat ? " (repeat)" : "", length, string.Join(", ", ints)); 
     }; 

     Parallel.For(0, 10, action); 
    } 

    public void Dispose() 
    { 
     threadRNG.Dispose(); 
    } 
} 

class RandomGenerator : Random 
{ 
    public RandomGenerator() : base(Guid.NewGuid().GetHashCode()) 
    { 
    } 

    public int NextInt(int maxValue) 
    { 
     return base.NextDouble() >= .5 ? base.Next(maxValue) : -base.Next(maxValue); 
    } 
} 

並列ループの複数の実行の間にThreadLocalを維持するのは良いことか悪いのですか?私は、RandomGeneratorの未使用のインスタンスが蓄積するかもしれないと思います。特に私は何千人もの執行をしています。

更新: 私は別のThreadLocalコンストラクタで別のバージョンのテストを試み、すべての値にアクセスできるようにしました(私は上記のコードを変更しました)。

また、myClass.Execute()を100,000回実行したところ、RandomGeneratorインスタンスが17個しか作成されていませんでした。だから、私は(私が望む)そのようなアプローチですべてがOKだと思う。

+0

スレッドが完了すると、ThreadLocal.Dispose()を呼び出すことになっています。私はどちらも気にしないでしょう。たぶん、これは少し過剰なもので、あなたはランダムなスレッドセーフを行うことができますか?それはちょうどロックを取る。このような偽のコードでは、タイミングはそれほど良いものではありませんが、それはあまり意味がありません。 –

+0

@HansPassant、私は[this](https://blogs.msdn.microsoft.com/pfxteam/2009/02/19/getting-random-numbers-in-a-thread-safe-way/)に私のコードを基づいています。と[this](https://codeblog.jonskeet.uk/2009/11/04/revisiting-randomness/)の記事をご覧ください。私は遺伝的アルゴリズムでそれを使用する予定なので、タイミングは重要です。この場合、ロックを使う方が良いとは思わない。 – quicktrick

+0

さて、明らかなことをしてください。あなたの気持ちを良くするために、ThreadLocalのDispose()メソッドを呼び出します。または、あなたがそれのコストを見過ごすことはないからです。それはあなた次第です。 –

答えて

1

スレッド単位の値は、すべての値を指定する必要がない場合は、スレッドをルーツにしています。したがって、スレッドが死んでも、値を保持することはできません。あなたのコードは問題ありません。

[ThreadStatic] static RandomGenerator rng;を使用するとさらに簡単になります。

スレッドローカルアクセスは少し遅いことに注意してください。 RNGを可能な限り小さくして、しばらくの間(各スレッドが構造的に可能な限り)RNGを引っ張るようにすれば、より良いかもしれません。

また、.NET RNGは実際には低速で低品質です。 XorShiftとバリアントの使用を検討してください。

+0

"スレッドが消滅しても、値を有効に保ちません。"私はそれを知らなかった、ありがとう!私はこの理由でThreadStaticの使用を避けました(とにかく、使用後に静的変数にnullを割り当てることはできます)。 XorShiftの使用を検討します。あなたの答えをもう一度ありがとう! – quicktrick

関連する問題