为什么彼此创建的两个任务生成相同的随机值? [重复]

问题描述 投票:36回答:2

此问题已经在这里有了答案:

Task.Factory.StartNew(() =>
    {
    new Class1();
    })

Task.Factory.StartNew(() =>
    {
    new Class2();
    })

在class1和class2的构造函数中,我有:

var timeout = new Random().Next(0, 5000);
Debug.Print(timeout.ToString());

两个类中的随机值'timeout'始终相同。我不明白为什么。

如果我在创建任务之间添加一个暂停,则不一样。

编辑:

我不知道这与“ Random String Generator Returning Same String”有什么关系。

他们正在方法中创建随机实例。我在完全不同的任务中称呼它,因此它们应该彼此独立。

c# random
2个回答
57
投票

我不知道这与“返回相同字符串的随机字符串生成器”有什么关系。

虽然根本原因相同,但没有直接关系。一个更好的副本是这个问题:Why do I keep getting two of same random values in this code?

它包含an explanation的功能new Random –出于documentation的礼貌:

默认种子值来自系统时钟,并且具有有限的分辨率。结果,通过调用默认构造函数紧密连续创建的不同Random对象将具有相同的默认种子值,因此将产生相同的随机数集。

换句话说:如果快速连续创建Random对象,它们将产生相同的随机数序列。

他正在方法中创建随机实例。我在完全不同的任务中称呼它,因此它们应该彼此独立。

这些对象是否在不同的线程(或Task)中创建是无关紧要的-它们仅取决于创建它们时的系统时间,而无其他依赖。正如您所说,它们实际上彼此独立。但是它们都依赖于相同的种子值,即创建时的系统时间。


解决这个问题的正确方法

通常只有Random类的一个实例。 –实际上,这样的代码是这样的:new Random().Next(…)是代码异味,因为它滥用Random类:不应为每个调用都生成一个新实例;相反,您应该重用同一实例来生成sequence随机数。[不幸的是,您不能简单地在不同的并发任务中使用相同的Random实例,因为relevant method并不是线程安全的-也就是说,同时从多个线程中调用它可能会导致竞争。有几种解决方法,但是最简单的方法是使用显式锁:

public Class(Random rng) { lock (rng) { var timeout = rng.Next(5000); Debug.Print(timeout.ToString()); } }

需要特别注意的是,必须锁定对rng的访问权限[[every
,否则就没有意义了。

现在您可以创建任务并运行它们,并获得适当的随机性:

var rng = new Random(); var tasks = new [] { Task.Run(() => { new Class(rng); }), Task.Run(() => { new Class(rng); }) }; Task.WaitAll(tasks);

注意,当忽略lock(…)块时,它可能

似乎
就像您得到正确的结果一样。这是使用并发性和随机性的危险:很难验证您的结果是否正确或在整个过程中是否被破坏。因此请谨慎行事。

12
投票
© www.soinside.com 2019 - 2024. All rights reserved.