在.Net 3.5中实现随机提供程序

时间:2012-03-16 17:25:22

标签: c# .net .net-3.5 random

以下是Skeet posting for a random provider

的高潮
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的帮助,您如何修改代码以获得线程安全的随机提供程序?

更新

好的,我现在和Simon的[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))); }
}

3 个答案:

答案 0 :(得分:9)

  

如果没有ThreadLocal的帮助,您如何修改代码以获得线程安全的随机提供程序?

Jon在您关联的文章中回答了您的问题:

  

使用一个实例,但也使用每个调用者在使用随机数生成器时必须记住获取的锁。这可以通过使用为您执行锁定的包装器来简化,但在多线程系统中,您仍然可能浪费大量时间等待锁定。

所以每次在包装器中锁定它,并在完成后解锁它。

如果这个足够便宜,很好,它足够便宜。

如果那不够便宜,那么你有两个选择。首先,使它更便宜。其次,编写一个伪随机数生成器的线程安全实现,可以在不锁定的情况下使用

有很多方法可以降低它的价格。例如,你可以换空间时间;你可以在程序启动时生成一个十万个随机数的数组,然后编写一个无锁算法,从数组中提供先前计算的随机值。当您用完值时,在数组中生成另外十万个值,并将旧数组换成旧数组。

它的缺点是它的内存消耗量大约是它的十万倍,并且每十万个数字突然变得非常慢,然后再次加速。如果这是不可接受的,那么会提出一个可接受的策略。你是那个知道什么是可接受的表现而不是什么的人。

或者,就像我说的那样,如果你不喜欢为你提供的那个,请自己写。使用可接受的性能编写Random的线程安全实现,并从多个线程中使用它。

  

我看到了Jon所说的关于锁定包装器的内容,但不确定代码的样子!

类似的东西:

sealed class SafeRandom
{
    private Random random = new Random();
    public int Next()
    {
        lock(random)
        {
            return random.Next();
        }
    }
}

现在每次拨打“下一步”时,都会锁定。 (总是锁定私有对象;这样你知道你的代码是锁定它的唯一代码!)如果两个线程同时调用Next,那么“失败者”阻止,直到“胜利者”离开下一个方法。

如果您愿意,甚至可以将SafeRandom对象设为静态类:

static class SafeRandom
{
    private static Random random = new Random();
    public static int Next()
    {
        lock(random)
        {
            return random.Next();
        }
    }
}

现在您可以从任何线程调用SafeRandom.Next()。

答案 1 :(得分:8)

您可以使用ThreadStaticAttribute

[ThreadStatic]
private static Random _random;

private static Random Random
{
    get
    {
        int seed = Environment.TickCount;

        return _random ?? (_random = new Random(Interlocked.Increment(ref seed)))
    }
}

答案 2 :(得分:1)

我刚刚阅读了“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