c# 如何生成随机数

最常用方式是复用同一Random实例,避免循环中重复创建;多线程应使用Random.Shared(.NET 6+)或ThreadLocal;安全场景必须用RandomNumberGenerator。

Random 类生成基本随机数

最常用也最直接的方式是实例化 Random 类,然后调用它的方法。注意:不要在循环里反复新建 Random 实例,否则可能因种子相同导致重复序列。

推荐做法是复用同一个实例,尤其在高频调用场景下:

var rand = new Random();
int num = rand.Next();           // 0 到 int.MaxValue 之间
int range = rand.Next(1, 101);   // 1 到 100(不含 101)
double d = rand.NextDouble();    // [0.0, 1.0)

Random 在多线程下不安全

Random 实例不是线程安全的。多个线程同时调用 Next() 可能抛出 InvalidOperationException 或返回异常值(如负数、超限值)。

解决方案有三种,按推荐顺序:

  • ThreadLocal 每线程隔离实例
  • 改用 Random.Shared(.NET 6+,静态、线程安全)
  • 加锁(性能差,仅临时应急)

.NET

6 起推荐写法:

int safeNum = Random.Shared.Next(1, 101);

需要密码学安全?别用 Random

RandomRandom.Shared 都是伪随机,可预测,**绝不能用于生成 Token、加密密钥、验证码盐值等安全敏感场景**。

应改用 System.Security.Cryptography.RandomNumberGenerator

var bytes = new byte[4];
RandomNumberGenerator.Fill(bytes);
int cryptoRand = BitConverter.ToInt32(bytes) & 0x7FFFFFFF;

这个值不可预测,但开销比 Random 高一个数量级,只在真正需要时用。

常见陷阱和兼容性注意

容易被忽略的点:

  • Next(min, max)max 是**排他上限**,比如 Next(0, 5) 只返回 0–4
  • .NET Framework 与 .NET Core/.NET 5+ 的 Random 算法不同,同一种子产生的序列不一致
  • Random 传固定种子(如 new Random(42))适合测试,但线上必须省略参数或用系统时间
  • Unity 中若在 Update() 里每帧 new Random(),大概率得到一串相同数字

种子逻辑本身不难,但错一次就可能让“随机”变成“固定”,尤其在分布式或高并发服务里,得盯紧实例生命周期和调用上下文。