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))); }
}

Implementing a Random provider in .Net 3.5

如果没有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