随机数和线程本地存储

时间:2010-09-21 03:18:26

标签: .net multithreading random

this question接受的答案,以及今天工作中的类似讨论让我对某些事情感到疑惑。

问题是如何在多线程程序中安全地生成随机数。接受的答案主张使用线程本地存储,有效地为每个线程创建一个随机数生成器。我想知道这是不是真的好主意。

假设我们有两个线程同时启动(很可能在多核系统上启动),并且都调用默认的Random构造函数来创建并初始化线程本地存储中的随机数生成器。由于它们没有传递种子参数,Random使用系统时间作为种子。因此,两个随机数生成器都已使用相同的种子进行初始化。它们都会产生相同的随机数序列。

由于这些线程是从线程池分配的,因此无法将特定对象与特定线程相关联。或者,在上述问题的情况下,您无法保证哪个池线程将执行下一个请求。所以想象一下发生以下情况:

At startup, two requests come in simultaneously.
Two threads are created, each initializing a random number generator with the same seed.

Each thread generates three random numbers.  They will be identical in both threads.

Next request comes in.  It's assigned to thread #1.
It generates a random number and exits.

Some period of time elapses.

Next request comes in.  It's assigned to thread #2.
It generates the same random number that thread #1 did just a while ago.

这可能会无限期地持续下去,尽管我怀疑它的乒乓球会非常糟糕。关键是两个线程具有相同的PRNG并且重复序列的可能性非常高。我知道PRNG中的P代表“伪”,但这有点多了。

我认为多个线程很可能使用相同的种子值初始化Random实例。如果发生这种情况,那么应用程序中至少某些东西的“随机性”将会受到影响。当然,其含义取决于应用程序。

我不知道的是,如果用不同的种子初始化PRNG,是否会使客户看到的序列更随机,更少随机或大致相同?也就是说,如果我写的话:

var rnd1 = new Random(123);
var rnd2 = new Random(654);
for (int i = 0; i < OneMillion; ++i)
{
    numbers.Add(rnd1.Next());
 numbers.Add(rnd2.Next());
}

我生成的数字序列是否会比我从任何一个PRNG产生200万的时候更多或更少随机?

2 个答案:

答案 0 :(得分:1)

生成的数字仅与您提供的种子一样随机。如果两个线程以相同的种子结束,则它们将具有完全相同的“随机”数字序列。

为防止此使用同步,请确保为每个TLS存储的随机数生成器提供唯一的种子。

private static object _sync = new object();
[ThreadStatic]
private static Random _rand;

...

if (_rand == null) {
    lock(_sync) {
        _rand = new Random(DateTime.Now.Ticks);
        Thread.Sleep(_rand.Next(0,3));
    }
}

还有其他方法可以确保种子是独一无二的,没有睡觉,但这是一种对演示有用的简单方法。

另一种选择,在我看来是一个更好的选择,就是使用一个随机数生成器并同步调用它。每个人都担心同步会导致性能差异,但除非您生成数百个随机数生成器一毫秒,否则同步不会增加任何明显的性能降级(在我的笔记本电脑上我可以获得并释放锁定17,000次一毫秒)。

答案 1 :(得分:1)

随机性水平大致相同,因为两个系列都是由same algorithm生成的。

你如何定义随机性?一个系列是否随机出现更随机可能取决于用户,以及该应用对该系列数字的作用。

如果您担心多个随机数生成器使用相同的种子,您可以始终从另一个单个生成器生成的序列中播种所有随机数生成器。这样,至少你的初始起点有点武断。