Blu*_*eft 76
不,使用来自多个线程的相同实例可能导致它中断并返回所有0.但是,创建一个线程安全的版本(每次调用时Next()
都不需要讨厌的锁)很简单.改编自本文中的想法:
public class ThreadSafeRandom
{
private static readonly Random _global = new Random();
[ThreadStatic] private static Random _local;
public int Next()
{
if (_local == null)
{
lock (_global)
{
if (_local == null)
{
int seed = _global.Next();
_local = new Random(seed);
}
}
}
return _local.Next();
}
}
Run Code Online (Sandbox Code Playgroud)
我们的想法是static Random
为每个线程保留一个单独的变量.然而,以明显的方式做到这一点失败了,因为另一个问题Random
- 如果几乎同时创建多个实例(在大约15ms内),它们将返回相同的值!为了解决这个问题,我们创建了一个全局静态Random
实例来生成每个线程使用的种子.
顺便提一下,上面的文章中有代码证明了这两个问题Random
.
Meh*_*ari 25
Next
实现线程安全的方法没有什么特别之处.但是,它是一个实例方法.如果不共享Random
不同线程的实例,则不必担心实例中的状态损坏.Random
如果不保留某种独占锁,则不要在不同线程中使用单个实例.
Jon Skeet在这个主题上有几个不错的帖子:
正如一些评论员所指出的那样,使用Random
线程排除的不同实例存在另一个潜在的问题,但是它们是相同的种子,因此会导致相同的伪随机数序列,因为它们可能同时创建或在紧密时间内创建彼此接近.缓解该问题的一种方法是使用主Random
实例(由单个线程锁定)来生成一些随机种子并Random
为每个其他要使用的线程初始化新实例.
JSW*_*ork 19
从微软的官方答案是非常强的没有.来自http://msdn.microsoft.com/en-us/library/system.random.aspx#8:
随机对象不是线程安全的.如果您的应用程序从多个线程调用随机方法,则必须使用同步对象以确保一次只有一个线程可以访问随机数生成器.如果不确保以线程安全的方式访问Random对象,则对返回随机数的方法的调用将返回0.
正如文档中所描述的,当多个线程使用相同的Random对象时,可能会发生非常讨厌的副作用:它只是停止工作.
(即存在一种竞争条件,当触发时,'random.Next ....'方法的返回值对于所有后续调用将为0.)
Evg*_*eny 19
C# 的 Random.Next() 方法线程安全吗?
正如之前所写,答案是否定的。然而,从.NET6开始,我们有了开箱即用的线程安全替代方案:Random.Shared.Next();
Guf*_*ffa 14
不,这不是线程安全的.如果需要使用来自不同线程的相同实例,则必须同步使用.
不过,我真的看不出你为什么会这么做的原因.每个线程拥有自己的Random类实例会更有效.
小智 8
另一种线程安全的方法是使用ThreadLocal<T>
如下:
new ThreadLocal<Random>(() => new Random(GenerateSeed()));
Run Code Online (Sandbox Code Playgroud)
GenerateSeed()
每次调用时,该方法都需要返回一个唯一值,以确保随机数序列在每个线程中都是唯一的.
static int SeedCount = 0;
static int GenerateSeed() {
return (int) ((DateTime.Now.Ticks << 4) +
(Interlocked.Increment(ref SeedCount)));
}
Run Code Online (Sandbox Code Playgroud)
适用于少量线程.
由于Random
不是线程安全的,因此每个线程应该有一个,而不是全局实例.如果您担心这些多个Random
类同时播种(即通过DateTime.Now.Ticks
或类似),您可以使用Guid
s来播种每个类..NET Guid
生成器需要相当长的时间来确保不可重复的结果,因此:
var rnd = new Random(BitConverter.ToInt32(Guid.NewGuid().ToByteArray(), 0))
Run Code Online (Sandbox Code Playgroud)
更新从.NET 6开始Random.Shared提供了内置的线程安全Random
类型(使用ThreadStatic
幕后进行同步)。
原始答案使用以下命令重新实现 BlueRaja 的答案ThreadLocal
:
public static class ThreadSafeRandom
{
private static readonly System.Random GlobalRandom = new Random();
private static readonly ThreadLocal<Random> LocalRandom = new ThreadLocal<Random>(() =>
{
lock (GlobalRandom)
{
return new Random(GlobalRandom.Next());
}
});
public static int Next(int min = 0, int max = Int32.MaxValue)
{
return LocalRandom.Value.Next(min, max);
}
}
Run Code Online (Sandbox Code Playgroud)