Implementing a Random provider in .Net 3.5
本文关键字:Net in provider Random Implementing | 更新日期: 2023-09-27 18:28:07
这是一个随机提供者的Skeet帖子的高潮:
public static class RandomProvider
{
private static int seed = Environment.TickCount;
private static ThreadLocal<Random> randomWrapper = new ThreadLocal<Random>(() =>
new Random(Interlocked.Increment(ref seed))
);
public static Random GetThreadRandom()
{
return randomWrapper.Value;
}
}
我想在.NET 3.5项目中使用相同的概念,所以ThreadLocal不是一个选项。
如果没有ThreadLocal的帮助,您将如何修改代码以获得线程安全的随机提供程序?
更新
好吧,我现在就用西蒙的[ThreadStatic],因为我最了解它。在时间允许的情况下,这里有很多好的信息可以回顾和重新思考。谢谢大家!
public static class RandomProvider
{
private static int _seed = Environment.TickCount;
[ThreadStatic]
private static Random _random;
/// <summary>
/// Gets the thread safe random.
/// </summary>
/// <returns></returns>
public static Random GetThreadRandom() { return _random ?? (_random = new Random(Interlocked.Increment(ref _seed))); }
}
如果没有ThreadLocal的帮助,您将如何修改代码以获得线程安全的随机提供程序?
Jon在你链接到的文章中回答了你的问题:
使用一个实例,但也要使用一个锁,每个调用者在使用随机数生成器时都必须记住获取该锁。这可以通过使用一个为您执行锁定的包装器来简化,但在一个多线程系统中,您仍然可能会浪费大量时间等待锁定。
所以每次都把它锁在包装器里,完成后再解锁。
如果它足够便宜,那就太好了,它足够便宜了。
如果这还不够便宜,那么你有两个选择。首先,使其更便宜。其次,编写一个伪随机数生成器的线程安全实现,该生成器可以在不锁定的情况下使用。
有很多方法可以使它更便宜。例如,你可以用空间换取时间;当程序启动时,您可以生成一个由十万个随机数组成的数组,然后编写一个无锁算法,从该数组中提供以前计算的随机值。当值用完时,在数组中再生成十万个值,并将新数组替换为旧数组。
它的缺点是,它的内存消耗量比它可能的大大约十万倍,而且每十万个数字,它就会突然变得非常慢,然后再次加速。如果这是不可接受的,那么提出一个可接受的策略。你知道什么是可以接受的表现,什么不是。
或者,就像我说的,如果你不喜欢为你提供的,写你自己的。编写一个性能可接受的Random的线程安全实现,并在多个线程中使用它。
我看到了Jon所说的关于锁定包装器的内容,但不确定代码会是什么样子!
类似于:
sealed class SafeRandom
{
private Random random = new Random();
public int Next()
{
lock(random)
{
return random.Next();
}
}
}
现在,每次你打电话给Next,你都会拿出一把锁。(总是锁定私有对象;这样你就知道你的代码是唯一锁定它的代码!)如果两个线程"同时"调用Next,那么"loser"会阻塞,直到"winer"离开Next方法。
如果你愿意,你甚至可以让SafeRandom对象成为一个静态类:
static class SafeRandom
{
private static Random random = new Random();
public static int Next()
{
lock(random)
{
return random.Next();
}
}
}
现在您可以从任何线程调用SafeRandom.Next()。
您可以使用ThreadStaticAttribute
[ThreadStatic]
private static Random _random;
private static Random Random
{
get
{
int seed = Environment.TickCount;
return _random ?? (_random = new Random(Interlocked.Increment(ref seed)))
}
}
我刚刚阅读了"C#in Depth"的链接,尽管我同意Random不线程安全是一种痛苦,但实际上我会使用不同的方法来解决这个问题,即出于性能原因。
实例化随机引擎是一项相当繁重的操作,所以我宁愿只保留一个实例,并通过使用锁使其线程安全:
public static class RandomProvider
{
private static Random randomEngine = new Random(Environment.TickCount);
private static object randomLock = new object();
public static int GetRandomValue()
{
lock(randomLock)
{
return randomEngine.Next();
}
}
}
HTH