中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何正確的在C# 中使用Random類

發布時間:2020-11-27 14:57:52 來源:億速云 閱讀:311 作者:Leah 欄目:開發技術

如何正確的在C# 中使用Random類?相信很多沒有經驗的人對此束手無策,為此本文總結了問題出現的原因和解決方法,通過這篇文章希望你能解決這個問題。

Random類介紹

Random類一個用于產生 偽隨機 數字的類。這里的偽隨機表示有隨機性但是可以基于算法模擬出隨機規律。

Random類的構造方式有兩種。

  • Random r= new Random()。會以當前系統時間作為默認種子構建一個隨機序列

  • Random r = new Random(unchecked((int)DateTime.Now.Ticks));。自定義一個種子,通常會使用時間Ticks。

隨機性保證

由于Random的 偽隨機 性,所以如果多個Random隨機序列生成的時間間隔很短(官方說法15ms內),那么他們產生的隨機數會大概率相同。如下列代碼

 /// <summary>
  /// 錯誤的Random構建。
  /// </summary>
  public static void Bad_Random()
  {
    //正確做法應當將 Random構建防止循環外。
    //Random創建間隔時間極短的情況下,隨機算法序列會基本一致,倒是隨機性也是一致的
    //var r = new Random();
    for (int i = 0; i < 10; i++)
    {
      var r = new Random();
      var val = r.Next(1, 100);
      Console.WriteLine(val);
    }
  }

運行結果:

如何正確的在C# 中使用Random類

所以在生產中通常可以考慮將Random單例化,以保證其隨機算法的序列獨一性。這也是官方推薦的方式。

Instead of instantiating individual Random objects, we recommend that you create a single Random instance to generate all the random numbers needed by your app.

這個問題在.net core下官方組件已對Random的構建作優化,所以上面的案例代碼如果放在.net core項目下運行,你會發現可以正確的生成隨機數。有興趣的小伙伴可以自己嘗試一下。不過為了代碼的延續性,還是建議Random作為單例模式設計。

那么將Random設計為單例是否就解決了隨機性的問題了呢,這時候就涉及到另外一個問題,Random不是線程安全的。如下列代碼

  /// <summary>
  /// 生成一個10位隨機數
  /// 設定了一定的復雜性,保證單線程下隨機數不重復
  /// </summary>
  /// <param name="random">Random.</param>
  /// <returns>隨機數.</returns>
  private static string GenerateRandomStr(Random random)
  {
    string source = "ABCDEFGHIKLMNOPQRTUVWXYZabcdefghiklmnopqrtuvwxyz";
    int length = 10;
    var list = Enumerable.Repeat(source, length)
       .Select(s => s[random.Next(s.Length)]).ToArray();
    return new string(list);
  }
  /// <summary>
  /// 單線程基本可以保證唯一性
  /// </summary>
  public static void Good_Random_In_SingleThread()
  {
    //正確做法應當將 Random構建防止循環外。
    //Random創建間隔時間極短的情況下,隨機算法序列會基本一致,倒是隨機性也是一致的
    var r = new Random();
    ConcurrentBag<string> list = new ConcurrentBag<string>();
    for (int i = 0; i < 20000; i++)
    {
      var val = GenerateRandomStr(r);
      list.Add(val);
    }

    Console.WriteLine($"單線程下重復數據有:{20000 - list.Distinct().Count()}");
  }

  /// <summary>
  /// 多線程下的Random構建。
  /// Bad案例,Random非線程安全
  /// 多線程高并發情況下,會出現概率重復
  /// </summary>
  public static void Bad_Random_In_MultThreads()
  {
    var r = new Random(unchecked((int)DateTime.Now.Ticks));
    ConcurrentBag<string> list = new ConcurrentBag<string>();

    var t1 = Task.Run(() =>
    {
      for (int i = 0; i < 10000; i++)
      {
        var val = GenerateRandomStr(r);
        list.Add(val);
      }
    });

    var t2 = Task.Run(() =>
    {
      for (int i = 0; i < 10000; i++)
      {
        var val = GenerateRandomStr(r);
        list.Add(val);
      }
    });

    Task.WaitAll(t1, t2);

    Console.WriteLine($"線程1和線程2的重復數據有:{20000 - list.Distinct().Count()}");
  }

運行結果:

如何正確的在C# 中使用Random類

這種重復率在生產環境上是不可接受的。那么產生的原因是什么呢?根源還是在 偽隨機線程不安全 上。我們可以想象下,一個Random實例中基于隨機算法產生的一個隨機數序列,在單線程下pop出一個隨機數,然后指向下一個隨機數。而在高并發的多線程情況下,指向下一個隨機數的動作還未完成時,另一個線程又來請求pop,這樣相同的隨機數被重復pop了。

網上有很多多線程下Random的解決方案,我查閱了一些感覺都不是很好。以下是我的解決方案。用到了 ThreadLocal 。這個類詳細的作用大家可以自己去查閱,這里大家只需要知道這個類可以保證它包含的對象只能線程內獨享。簡單說,同一類型對象 每個線程都獨有一個Random實例互不影響。

 //利用ThreadLocal 實現每個線程下Random獨有
  //再通過seed原子性變更,保證每個Random的seed不同而生成的隨機數列也不同
  private static int seed = 100;
  private static ThreadLocal<Random> threadLocal = new ThreadLocal<Random>(() => new Random(Interlocked.Increment(ref seed)));

  /// <summary>
  /// 多線程下的Random構建。
  /// </summary>
  public static void Good_Random_In_MultThreads()
  {
    ConcurrentBag<string> list = new ConcurrentBag<string>();

    var t1 = Task.Run(() =>
    {
      for (int i = 0; i < 10000; i++)
      {
        var val = GenerateRandomStr(threadLocal.Value);
        list.Add(val);
      }
    });

    var t2 = Task.Run(() =>
    {
      for (int i = 0; i < 10000; i++)
      {
        var val = GenerateRandomStr(threadLocal.Value);
        list.Add(val);
      }
    });

    Task.WaitAll(t1, t2);

    Console.WriteLine($"[ThreadLocal模式]線程1和線程2的重復數據有:{20000 - list.Distinct().Count()}");
  }

運行結果:

如何正確的在C# 中使用Random類

由此可見,基于ThreadLocal的特性,并區別了每個線程下的seed都不一樣,從而保證每個Random的隨機性也不行一樣。

那么到這里Random的隨機性問題解決了嗎??

再深入思考下,對于集群部署情況,多臺服務器同時運行,上述的Random隨機性能保證嗎?聰明的小伙伴應該能想到在不同服務器上,由于初始seed相同,可能又導致Random的隨機性相同的情況發生。

那么解決方案也很簡單,保證每臺服務器的初始seed不同即可。這里的解決方案很多,不限于機器編號、IP地址后幾位、啟動時間(Environment.TickCount)等等。

看完上述內容,你們掌握如何正確的在C# 中使用Random類的方法了嗎?如果還想學到更多技能或想了解更多相關內容,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

西城区| 乌拉特中旗| 涡阳县| 四会市| 石渠县| 兴义市| 兴宁市| 江源县| 尖扎县| 横峰县| 东光县| 冕宁县| 姜堰市| 榕江县| 临安市| 合作市| 深泽县| 霍邱县| 天镇县| 凭祥市| 万全县| 永丰县| 凤凰县| 奉贤区| 建瓯市| 岳阳市| 郴州市| 博罗县| 浏阳市| 阜宁县| 高青县| 洮南市| 奉化市| 北川| 鹤山市| 兴业县| 平原县| 讷河市| 兴仁县| 绍兴市| 马边|