线程安全的高性能随机发生器

bea*_*der 1 c# random multithreading

我需要一个线程安全的高性能随机数生成器。我只需要值类型(ulong现在是)中的随机字节,而不是在范围内。我使用了 C# 内置Random类,但它有点慢而且不是线程安全的。

后来我转向实际上工作得很好的 XORShift 函数,但为了实现线程安全,我需要将计算放入 中lock,这会大大降低性能。

我用来生成随机ulong数的内容如下:

public class Rand
{
    ulong seed = 0;
    object lockObj = new object();

    public Rand()
    {
        unchecked
        {
            seed = (ulong)DateTime.Now.Ticks;
        }
    }

    public Rand(ulong seed)
    {
        this.seed = seed;
    }

    public ulong GetULong()
    {
        unchecked
        {
            lock (lockObj)
            {
                ulong t = 0;

                t = seed;
                t ^= t >> 12;
                t ^= t << 25;
                t ^= t >> 27;
                seed = t;

                return t * 0x2545F4914F6CDD1D;
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这工作得很好而且很快,但是1-2us如果从 200 个并发线程调用它,则需要锁定,否则计算会在100ns.

如果我取消锁定,则有可能两个线程采用相同的种子并计算相同的随机数,这对我的目的不利。如果我删除ulong t声明并直接在种子上工作,那么为两个并发调用生成相同随机数的机会很小,但也有可能将值从值范围移出,就像t << 25将被不同的线程连续调用多次而不携带旋转它会变成简单的 0。

我认为正确的方法是,如果有一个共享值可能会被任何并发调用更改并在计算方法中使用该值,因为这些值是原子的(至少对于 CPU 内核),如果有很多,这不是问题计算同时使用它,但如果此值从位范围移出,则会出现问题。

有什么好的解决方案可以解决这个问题吗?我会很感激任何帮助。

编辑:好的,我忘了提到我无法控制线程,因为异步任务正在调用这个函数,所以线程是从线程池中随机来的,使用线程 ID 也是一个没有解决方案,因为有机会特定线程永远不会再次调用此方法,并且为该 ID 保留一个实例并不是一件好事。

l33*_*33t 7

只需Rand在每个线程上创建一个实例。线程安全,无锁定,因此非常高效。这可以使用ThreadStaticAttribute来实现。

public static class Rand
{
    [ThreadStatic] private static Rand defaultRand;
    public static Rand Default => defaultRand ??= new Rand();
    // Add extra methods for seeding the static instance...
}

// Then in any thread:
var randomNumber = Rand.Default.GetULong();
Run Code Online (Sandbox Code Playgroud)