7

我剛開始玩任務並行庫,並遇到有趣的問題;我對正在發生的事情有一個總體概念,但希望聽到比我更能勝任的人的意見,幫助我們瞭解正在發生的事情。我對有點冗長的代碼表示歉意。並行循環和隨機產生奇數結果

我開始用的隨機遊走非並行模擬:

var random = new Random(); 
Stopwatch stopwatch = new Stopwatch(); 

stopwatch.Start(); 

var simulations = new List<int>(); 
for (var run = 0; run < 20; run++) 
{ 
    var position = 0; 
    for (var step = 0; step < 10000000; step++) 
    { 
     if (random.Next(0, 2) == 0) 
     { 
      position--; 
     } 
     else 
     { 
      position++; 
     } 
    } 

    Console.WriteLine(string.Format("Terminated run {0} at position {1}.", run, position)); 
    simulations.Add(position); 
} 

Console.WriteLine(string.Format("Average position: {0} .", simulations.Average())); 
stopwatch.Stop(); 

Console.WriteLine(string.Format("Time elapsed: {0}", stopwatch.ElapsedMilliseconds)); 
Console.ReadLine(); 

然後我寫了我在並行循環的第一次嘗試:

var localRandom = new Random(); 

stopwatch.Reset(); 
stopwatch.Start(); 

var parallelSimulations = new List<int>(); 
Parallel.For(0, 20, run => 
{ 
    var position = 0; 
    for (var step = 0; step < 10000000; step++) 
    { 
     if (localRandom.Next(0, 2) == 0) 
     { 
      position--; 
     } 
     else 
     { 
      position++; 
     } 
    } 

    Console.WriteLine(string.Format("Terminated run {0} at position {1}.", run, position)); 
    parallelSimulations.Add(position); 
}); 


Console.WriteLine(string.Format("Average position: {0} .", parallelSimulations.Average())); 
stopwatch.Stop(); 

Console.WriteLine(string.Format("Time elapsed: {0}", stopwatch.ElapsedMilliseconds)); 

Console.ReadLine(); 

當我跑了它在虛擬機上設置爲僅使用1個內核,我觀察到類似的持續時間,但不再按順序處理這些運行 - 毫不奇怪。

當我在雙核機器上運行它時,事情變得很奇怪。我看到時間沒有改善,並且在每次運行中都觀察到一些非常奇怪的結果。大多數運行結果爲-1,000,000(或非常接近),這表明Random.Next始終返回0。

當我做了隨機本地爲每個循環中,一切都工作得很好,我也得到了預期的持續時間提高:

Parallel.For(0, 20, run => 
     { 
      var localRandom = new Random(); 
      var position = 0; 

我的猜測是,這個問題是與事實做了隨機對象在循環之間共享,並且有一些狀態。 「失敗並行」版本的持續時間缺乏改進是因爲這樣一個事實,即對Random的調用不是並行處理的(即使我看到並行版本使用兩個核心,而原始版本不是) 。我真的沒有得到的是爲什麼模擬結果是他們是。

我有一個單獨的擔心是,如果我在每個循環中使用本地隨機實例,我可能會遇到多個循環以相同種子開始的問題(當您生成多個蘭姆時太緊密,導致相同的序列)。

對發生的事情的任何洞察對我來說都是非常寶貴的!

回答

2

這些方法都不會給你真正好的隨機數。

本博客文章涵蓋了很多的方法對漸入佳境的隨機數隨機

http://blogs.msdn.com/b/pfxteam/archive/2009/02/19/9434171.aspx

這些可能是罰款,許多日常應用。

但是,如果您在多個線程上使用相同的隨機數生成器即使使用不同的種子您仍然會影響隨機數的質量。這是因爲您正在生成可能重疊的僞隨機數序列。

此視頻解釋了爲什麼在更詳細一點:

http://software.intel.com/en-us/videos/tim-mattson-use-and-abuse-of-random-numbers/

如果你想真正的隨機數,那麼你真的需要使用加密的隨機數發生器System.Security.Cryptography.RNGCryptoServiceProvider。這是線程安全的。

+0

Ade,感謝您指向S. Toub文章的指針,它非常棒。 – Mathias 2010-06-25 05:29:34

2

Random類不是線程安全的;如果你在多個線程上使用它,它會變得混亂。

您應該在每個線程上單獨創建一個Random實例,並確保它們最終不會使用相同的種子。 (例如,Environment.TickCount * Thread.CurrentThread.ManagedThreadId

+0

你會如何處理種子問題? – Mathias 2010-05-27 20:32:29

+0

事件,如果你採用這種方法,有問題,我不會使用TickCount * ManageThreadId,因爲這會產生非常接近的種子。請參閱下面的答案,以獲取種子的更好方法。 – 2010-06-25 13:24:23

1

一個核心問題:

  • random.Next不是線程安全的。

兩個後果:隨機性

  1. 質量是由競爭條件被破壞。
  2. 虛假共享破壞多核的可擴展性。

幾種可能的解決方案:

  • random.Next線程安全:解決的質量問題,但沒有可擴展性。
  • 使用多個PRNG:解決可伸縮性問題,但可能會降低質量。
  • ...