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:解决可伸缩性问题,但可能会降低质量。
  • ...