gpt4 book ai didi

c# - 线程安全的高性能随机数发生器

转载 作者:行者123 更新时间:2023-12-03 13:14:11 37 4
gpt4 key购买 nike

我需要一个线程安全的高性能随机数生成器。我只需要值类型(目前为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;
}
}
}
}
这可以正常且快速地工作,但是如果从200个并发线程中调用它,则锁定大约需要 1-2us,否则计算将在 100ns下完成。
如果我取消锁定,则两个线程有​​可能获得相同的种子,并且将计算出相同的随机数,这对我的目的不利。如果我要删除 ulong t声明并直接在种子上工作,那么几乎没有机会为两个并发调用生成相同的随机数,但是也有可能该值会从值范围移出,例如 t << 25将被不同的线程连续调用多次,而不进行旋转,它将变成简单的0。
我认为正确的方法是,如果有任何共享值可以被任何并发调用更改并在计算方法中使用该值,因为这些值是原子性的(至少对于CPU内核而言),如果有很多共享值就不成问题计算在同一时间使用它,但是如果此值从位范围移出,那将是一个问题。
有什么好的解决方案可以解决这个问题吗?感谢您的帮助。
编辑:好的,我忘了提到我无法控制线程,因为异步任务正在调用此函数,因此线程是从线程池中随机发出的,使用线程ID也是没有解决方案的,因为有机会特定线程根本不会再调用此方法,并且为该ID保留实例不是一件好事。

最佳答案

您可以在不锁定的情况下做到这一点,并且仍然是线程安全的。假设计算非常快,并且执行速度较慢,那么在开始计算和完成计算之间,如果另一个线程对其进行了更改,则简单地重新计算可能会更快。您可以使用Interlocked.CompareExchange自旋循环来做到这一点。唯一的困难是没有超长版本,因此我们必须使用不安全的方法来获得等效版本。

private static unsafe ulong InterlockedCompareExchange(ref ulong location,
ulong value, ulong comparand)
{
fixed (ulong* ptr = &location)
{
return (ulong)Interlocked.CompareExchange(ref *(long*)ptr, (long)value, (long)comparand);
}
}

public ulong GetULong()
{
unchecked
{
ulong prev = seed;

ulong t = prev;
t ^= t >> 12;
t ^= t << 25;
t ^= t >> 27;

while (InterlockedCompareExchange(ref seed, t, prev) != prev)
{
prev = seed;
t = prev;
t ^= t >> 12;
t ^= t << 25;
t ^= t >> 27;
}

return t * 0x2545F4914F6CDD1D;
}
}

关于c# - 线程安全的高性能随机数发生器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64937914/

37 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com