随机生成数字1超过90%并行

Mat*_*ero 20 .net c# random parallel-processing

考虑以下程序:

public class Program
{
     private static Random _rnd = new Random();
     private static readonly int ITERATIONS = 5000000;
     private static readonly int RANDOM_MAX = 101;

     public static void Main(string[] args)
     {
          ConcurrentDictionary<int,int> dic = new ConcurrentDictionary<int,int>();

          Parallel.For(0, ITERATIONS, _ => dic.AddOrUpdate(_rnd.Next(1, RANDOM_MAX), 1, (k, v) => v + 1));

          foreach(var kv in dic)
             Console.WriteLine("{0} -> {1:0.00}%", kv.Key, ((double)kv.Value / ITERATIONS) * 100);
     }
}
Run Code Online (Sandbox Code Playgroud)

这将打印以下输出:

(注意每次执行时输出会有所不同)

> 1 -> 97,38%
> 2 -> 0,03%
> 3 -> 0,03%
> 4 -> 0,03%
...
> 99 -> 0,03%
> 100 -> 0,03%
Run Code Online (Sandbox Code Playgroud)

为什么数字1以这样的频率生成?

Bat*_*eba 21

Random不是线程安全的.

Next 没有什么特别的,以确保线程安全.

不要这样使用Random.并且不要考虑使用线程本地存储持续时间,否则你将搞乱生成器的统计属性:你必须只使用一个Random实例.一种方法是lock(_global)在该锁定区域中使用和绘制数字.

认为这里发生的事情是,到达生成器的第一个线程获得正确生成的随机数,并且所有后续线程都为每个绘图接收0.使用32个线程的"并行化"线程池,您在上面引用的比率大致得以实现; 假设31个线程的结果放在第一个桶中.


Jes*_*pez 6

从线程本地存储解决方案更进一步,并试图避免统计问题,我建议使用从RNGCryptoServiceProvider以下生成的随机种子:

using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
    class Program
    {

        private static readonly int ITERATIONS = 5000000;
        private static readonly int RANDOM_MAX = 101;

        private static int GetCriptoRandom()
        {
            using (var rng = new System.Security.Cryptography.RNGCryptoServiceProvider())
            {
                byte[] bytes = new byte[4];
                rng.GetBytes(bytes);
                return BitConverter.ToInt32(bytes, 0);
            }
        }

        private static ThreadLocal<Random> m_rnd = new ThreadLocal<Random>(() => new Random(GetCryptoRandom()));

        private static Random _rnd
        {
            get
            {
                return m_rnd.Value;
            }
        }

        static void Main(string[] args)
        {
            ConcurrentDictionary<int, int> dic = new ConcurrentDictionary<int, int>();
            Parallel.For(1, ITERATIONS, _ => dic.AddOrUpdate(_rnd.Next(1, RANDOM_MAX), 1, (k, v) => v + 1));
            foreach (var kv in dic)
                Console.WriteLine("{0} -> {1:0.00}%", kv.Key, ((double)kv.Value / ITERATIONS) * 100);

        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在统计学上看似正确,结果范围从0.99%到1.01%.