在Parallel.For中使用Random

1 c# random multithreading parallel.for

我的程序执行许多模拟,每个模拟都需要生成许多随机数。串行方法很简单并且有效。但是,在追求并行化工作的过程中,我相信我创造了一种比我能找到的方法更直接的方法。其他方法有些过时了,现在有可能以前无法做到。

我是否缺少使我的方法容易受到无数多线程问题困扰的东西?我的方法使用a的能力Parallel.For为单个线程实例化变量,因此不需要像我发现的其他方法那样的其他类。在这种情况下,每个线程都有自己的线程Random

定时:

我的方法:4秒

斯蒂芬:14岁

乔恩:16岁

显然,我对斯蒂芬或乔恩的了解不多,所以我担心自己错过了一些东西。

我的方法:

Random rnd = new Random();
int trials = 1_000_000;

private readonly object globalLock = new object();
ParallelOptions po = new ParallelOptions();
po.MaxDegreeOfParallelism = 4;

await Task.Run(() =>
{
    Parallel.For<Random>(0, trials, po, 
    () => { lock(globalLock){ return new Random(rnd.Next()); } }, 
    (i, loop, local) =>
    {
        for (int work = 0; work < 1000; work++)
        {
            local.Next();
        }

        return local;
    },
        (x) => { }
    );
});
Run Code Online (Sandbox Code Playgroud)

接下来的方法是MSDN Blog上的Stephen Toub :

public static class RandomGen2
{
    private static Random _global = new Random();
    [ThreadStatic]
    private static Random _local;

    public static int Next()
    {
        Random inst = _local;
        if (inst == null)
        {
            int seed;
            lock (_global) seed = _global.Next();
            _local = inst = new Random(seed);
        }
        return inst.Next();
     }
}

await Task.Run(() =>
{
    Parallel.For(0, trials, i =>
    {
        for (int work = 0; work < 1000; work++)
        {
            RandomGen2.Next();
        }
    });

});
Run Code Online (Sandbox Code Playgroud)

下一个方法是Jon Skeet在他的博客上

public static class ThreadLocalRandom
{
    private static readonly Random globalRandom = new Random();
    private static readonly object globalLock = new object();

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

    public static Random NewRandom()
    {
        lock (globalLock)
        {
            return new Random(globalRandom.Next());
        }
    }

    public static Random Instance { get { return threadRandom.Value; } }

    public static int Next()
    {
        return Instance.Next();
    }
}

await Task.Run(() =>
{
    Parallel.For(0, trials, i =>
    {
        for (int work = 0; work < 1000; work++)
        {
            ThreadLocalRandom.Instance.Next();
        }
    });
});
Run Code Online (Sandbox Code Playgroud)

更新/答案: Brian指出我使用的Jon方法不正确。一种更正确的方法是ThreadLocalRandom.Instance为每个Parallel.For循环调用一个并将该实例用于内部for循环。这样可以防止在每次调用时进行线程检查,而是每个Parallel.For循环仅执行一次线程检查。正确使用Jon的方法可使他的方法比Parallel.For我正在使用的重载更快。

Eri*_*ert 5

但是,在追求并行化工作的过程中,我相信我找到了一种比我所能找到的更为直接的方法。

它更直接,但是错误。

其他方法有些过时了。

这到底意味着什么?

我是否缺少使我的方法容易受到无数多线程问题困扰的东西?

线程安全的最基本规则是:如果没有锁,则不能在多个线程上使用非线程安全对象。 Random不是线程安全的,但是您在每个线程上使用相同的线程来计算种子。

请注意,乔恩和史蒂芬的“约会”方法正确地锁定了随机种子。

显然,我对斯蒂芬或乔恩的了解不多,所以我担心自己错过了一些东西。

首先,在编写任何多线程代码之前,您应该彻底内部化线程安全的基本规则。

第二,你的态度就是你的错误。正确的态度是:乔恩(Jon)和史蒂芬(Stephen)都是专家,他们的解决方案没有多余的部分。如果您认为找到的解决方案缺少其解决方案所具有的部分,那么您需要解释为什么您的解决方案不需要其解决方案所具有的部分

  • 谢谢回答。过时的意思是几岁,现在有可能以前无法做到。我确实错过了这个问题。我将修复问题。 (2认同)
  • @Sobachatina:没有敌意。问题是“此代码不安全吗?” 并给出了明确的答案:线程安全的最基本方面是错误的。**我以错误的代码为生**。我设计了可以识别错误代码的静态分析器,并且研究了“哪些用户因素导致人们编写错误代码”。错误代码生成的一个重要方面是一种态度,即无需“理解”现有解决方案即可对其进行“改进”。现在,如果您有想要看到的更好的答案,请*发布该答案*,然后我们都会从中受益。 (2认同)