# 背景

ChatGPT 插件在用户电脑上持续运行一段时间之后,回复率会激增至每条消息都回。
本来会随机回复,但是偶发这个奇怪的现象。我在排查代码许久没有找到原因,于是只能将随机数打印出来观察。今天用户告诉我问题又复现了,于是我观察日志惊讶的发现,随机数结果都是 0!那怪不得回复率那么高

# 原因

我挂了断点上去,发现结果确实是 0,于是我又用即时窗口尝试调用 Next 与 NextDouble,发现结果依然是 0。
于是网上找原因,发现相关内容很少,在 Stack Overflow 十年前的一个问题中提到,多线程访问 Random 对象会导致这一问题,于是我又前往微软官方文档寻找是否有相关描述,很可惜我并没有找到任何与线程安全相关的提示。

# 尝试解决

对于.NetCore2 + 版本来说,解决方案很简单,使用 Random.Shared 即可。
对于低版本.net 来说,解决方案可以分为加锁与其他实现。其中加锁会影响性能,所以此处提供使用 RNGCryptoServiceProvider 的解决方案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
private static RNGCryptoServiceProvider Rng { get; set; } = new();

public static int Next(int minValue, int maxValue)
{
if (minValue >= maxValue)
{
throw new ArgumentOutOfRangeException();
}

long diff = (long)maxValue - minValue;
byte[] uint32Buffer = new byte[4];
uint rand;
do
{
Rng.GetBytes(uint32Buffer);
rand = BitConverter.ToUInt32(uint32Buffer, 0);
}
while (rand >= (uint.MaxValue - (((uint.MaxValue % diff) + 1) % diff)));

return (int)(minValue + (rand % diff));
}

public static double NextDouble()
{
byte[] bytes = new byte[8];
Rng.GetBytes(bytes);
ulong ul = BitConverter.ToUInt64(bytes, 0) >> 11; // 53位精度
return ul / (double)(1UL << 53);
}

public static double NextDouble(double minValue, double maxValue)
{
if (minValue >= maxValue)
{
throw new ArgumentOutOfRangeException();
}

return minValue + (maxValue - minValue) * NextDouble();
}