这个StaticRandom类是线程安全的吗?

时间:2018-04-07 06:19:48

标签: c# multithreading asynchronous thread-safety

我正在编写的程序是一个游戏服务器,它将在单独的线程中有多个会话。为避免不必要的锁定等待时间,添加了[ThreadStatic]

该程序将包含一些异步功能和 ThreadSafeStaticRNG(此类)将在异步中使用。

public class ThreadSafeStaticRNG 
{
    [ThreadStatic]
    static Random r = new Random();

    /// <summary>
    /// get random number from [min,max)
    /// </summary>
    /// <returns></returns>
    public static int GetNext(int min, int max)
    {
        int n;
        lock (r)
        {
            n = r.Next(min, max);
        }
        return n;
    }
}

我需要验证;

  1. 据我所知,使用async / Task创建的工作线程不会有单独的ThreadStatic实例。所以锁是必要的。
  2. 如果上述情况属实,则仅lock中的限制是await无法使用lock。因此,在lock中使用async function是安全的。
  3. {li>(编辑)lock (r) r个实例,可以吗?我查看的官方文档和其他代码仅为object创建了其他lock

2 个答案:

答案 0 :(得分:2)

显示的代码是(过度)线程安全且不正确(可以生成相同/同步的随机值)。

ThreadStatic保证每个线程(从线程池中手工创建)都有自己的变量实例。因此,只要您不将该变量暴露在外(如代码中所示),您就不需要lock

每个时刻的每个线程只能运行一个函数 - 无论它是传递给线程的async方法还是函数的一部分。线程不会在执行过程中在函数之间切换,因此不能同时在同一个线程上对GetNext进行两次调用。

请注意,快速创建多个Random对象可导致Random number generator only generating one random number - 检查此特定answer以正确初始化此类每线程实例Random(使用增量种子)

答案 1 :(得分:1)

  1. 任务/异步可能没有分配给它的单独线程,并且可以在多个线程上执行任务
  2. 是的,使用Lock是安全的,并且使用非线程安全的Random 功能,这是必要的。没有必要混合Lock和 ThreadStatic。你在我的意图中使用Lock()的意图更明显 意见,并离开ThreadStatic。
  3. 正常创建一个新的Object并对其进行锁定,而不是对操作对象进行锁定,但在您的情况下,这样做并不正确。
  4. 也许更好的模式是反转问题并让调用者将新的Random()对象的实例传递到依赖于它的代码中。这将保证每个任务的Random的唯一实例,并且更具声明性和可测试性(通过IRandom并在单元测试中使用测试替代将是最好的整体解决方案。) - 对于单元测试,您需要可预测的结果,不是随机结果,因此通过IOC将已知数字生成器替换为随机数生成器的能力非常重要。