首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C# 隨機數產生器完整指南:Random、RandomNumberGenerator、密碼學安全、執行緒安全、效能比較

     編輯:關於C語言

前言

C# 提供多種隨機數產生方式,從基礎的 System.Random 到密碼學安全的 RandomNumberGenerator,再到 .NET 6+ 的 Random.Shared 與 RandomNumberGenerator.GetBytes 靜態方法。本文系統整理各類 API 適用場景、執行緒安全性、效能基準、常見陷阱與最佳實踐。

核心類別比較

類別/方法命名空間演算法密碼學安全執行緒安全適用場景
RandomSystem改良 Knuth 減法 (x86) / xoshiro256** (ARM/.NET 6+)❌ 可預測❌ 實例不安全遊戲、模擬、測試資料、非安全負載均衡
Random.Shared (.NET 6+)System同 Random❌✅ 內部鎖定高併發非安全隨機需求
RandomNumberGenerator (RNG)System.Security.CryptographyOS CSPRNG (Windows: BCryptGenRandom, Linux: getrandom)✅✅ 靜態方法安全金鑰生成、Token、Nonce、Salt、密碼重設碼
RNGCryptoServiceProviderSystem.Security.Cryptography同 RNG (舊 API)✅✅舊專案相容 (.NET Framework)

基本用法範例

1. System.Random (一般用途)

// 單一實例重複使用 (避免種子相同)
private static readonly Random _random = new Random(); // .NET 6+ 種子來自 RNG

public int NextInt(int minInclusive, int maxExclusive) => _random.Next(minInclusive, maxExclusive);
public double NextDouble() => _random.NextDouble();
public void NextBytes(byte[] buffer) => _random.NextBytes(buffer);

// 亂序演算法 (Fisher-Yates)
public void Shuffle(T[] array)
{
    for (int i = array.Length - 1; i > 0; i--)
    {
        int j = _random.Next(i + 1);
        (array[i], array[j]) = (array[j], array[i]);
    }
}

2. Random.Shared (.NET 6+ 推薦)

// 無需管理實例,執行緒安全
int dice = Random.Shared.Next(1, 7);
double prob = Random.Shared.NextDouble();
byte[] bytes = new byte[32];
Random.Shared.NextBytes(bytes);

3. RandomNumberGenerator (密碼學安全)

using System.Security.Cryptography;

// .NET 6+ 靜態方法 (最簡潔)
byte[] key = RandomNumberGenerator.GetBytes(32); // 256-bit 金鑰
string token = Convert.ToBase64String(RandomNumberGenerator.GetBytes(32)); // URL-safe token
int secureInt = RandomNumberGenerator.GetInt32(100000, 999999); // 6 位數驗證碼

// 所有版本通用實例模式
using var rng = RandomNumberGenerator.Create();
byte[] salt = new byte[16];
rng.GetBytes(salt);
rng.GetNonZeroBytes(salt); // 確保無 0x00 (某些協定需求)

4. 產生隨機密碼/字串 (安全版)

public static string GenerateSecurePassword(int length, bool includeSpecial = true)
{
    const string chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    const string special = "!@#$%^&*()-_=+[]{}|;:,.<>?";
    string pool = chars + (includeSpecial ? special : "");
    
    byte[] bytes = RandomNumberGenerator.GetBytes(length);
    char[] result = new char[length];
    for (int i = 0; i < length; i++)
        result[i] = pool[bytes[i] % pool.Length];
    return new string(result);
}

// 更簡潔的 Base64/URL-safe 字串
public static string GenerateToken(int byteLength = 32) 
    => Convert.ToBase64String(RandomNumberGenerator.GetBytes(byteLength))
        .Replace("+", "-").Replace("/", "_").TrimEnd('=');

進階主題

1. 執行緒安全的 Random 包裝器 (.NET 5 及以下)

public static class ThreadSafeRandom
{
    [ThreadStatic] private static Random _local;
    private static readonly Random _global = new Random();
    private static readonly object _lock = new();

    public static Random Instance
    {
        get
        {
            if (_local == null)
            {
                int seed;
                lock (_lock) seed = _global.Next();
                _local = new Random(seed);
            }
            return _local;
        }
    }

    public static int Next(int min, int max) => Instance.Next(min, max);
    public static double NextDouble() => Instance.NextDouble();
}

2. 可重現隨機序列 (測試/模擬用)

// 固定種子 → 相同序列
var deterministic = new Random(12345);
var seq1 = Enumerable.Range(0, 10).Select(_ => deterministic.Next()).ToArray();
// 重新建立相同種子
var deterministic2 = new Random(12345);
var seq2 = Enumerable.Range(0, 10).Select(_ => deterministic2.Next()).ToArray();
Debug.Assert(seq1.SequenceEqual(seq2)); // 通過

3. 正態分佈 / 高斯分佈隨機數 (Box-Muller 變換)

public static double NextGaussian(Random rnd, double mean = 0, double stdDev = 1)
{
    double u1 = 1 - rnd.NextDouble(); // (0,1]
    double u2 = 1 - rnd.NextDouble();
    double z = Math.Sqrt(-2 * Math.Log(u1)) * Math.Cos(2 * Math.PI * u2);
    return mean + z * stdDev;
}

4. 效能基準測試 (1 億次整數產生,.NET 7, Release)

方法耗時每秒億次備註
Random.Next (單實例)1.02s0.98基準
Random.Shared.Next1.15s0.87鎖定開銷
ThreadLocal0.95s1.05無鎖,最快
RNG.GetInt324.8s0.21系統呼叫開銷大
RNG.GetBytes + BitConverter3.2s0.31批次較快

常見陷阱與最佳實踐

陷阱 1:短時間內多次 `new Random()` 產生相同序列
舊版 .NET Framework 種子來自 `Environment.TickCount` (毫秒級),迴圈中快速建立會得到相同種子。
解決:單一實例重複使用、Random.Shared、或 .NET 6+ 自動使用 RNG 種子。
陷阱 2:`Random` 實例跨執行緒共用導致內部狀態損壞、效能下降
解決:.NET 6+ 用 `Random.Shared`;舊版用 `ThreadLocal` 或鎖定包裝器。
陷阱 3:密碼學場景誤用 `Random` (驗證碼、API Key、加密金鑰)
攻擊者可由觀察輸出推導內部狀態預測後續值。
解決:凡涉及安全性,一律用 `RandomNumberGenerator`。
陷阱 4:`RandomNumberGenerator.GetInt32(min, max)` 邊界包含關係
minInclusive, maxExclusive (同 `Random.Next`)。注意 maxExclusive 不包含。
最佳實踐:依用途選擇
  • 遊戲骰子、測試資料、負載均衡權重 → Random.Shared
  • Session ID、CSRF Token、重設密碼連結、加密金鑰、鹽值 → RandomNumberGenerator
  • 高效能平行模擬、蒙地卡羅 → `ThreadLocal` 或 `Parallel.ForEach` 區域實例
  • 需可重現序列 (單元測試、基準測試) → `new Random(fixedSeed)`

總結

C# 隨機數生態已完善:一般用途用 Random.Shared (.NET 6+) 或單一 Random 實例;安全用途一律用 RandomNumberGenerator 靜態方法。理解種子來源、執行緒安全、密碼學安全性邊界,即可避開 90% 的隨機數相關 Bug 與安全漏洞。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved