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);
}
}

运行结果:

所以在生产中通常可以考虑将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()}");
}

运行结果:

这种重复率在生产环境上是不可接受的。那么产生的原因是什么呢?根源还是在伪随机线程不安全上。我们可以想象下,一个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()}");
}

运行结果:

由此可见,基于ThreadLocal的特性,并区别了每个线程下的seed都不一样,从而保证每个Random的随机性也不行一样。

那么到这里Random的随机性问题解决了吗??

再深入思考下,对于集群部署情况,多台服务器同时运行,上述的Random随机性能保证吗?聪明的小伙伴应该能想到在不同服务器上,由于初始seed相同,可能又导致Random的随机性相同的情况发生。

那么解决方案也很简单,保证每台服务器的初始seed不同即可。这里的解决方案很多,不限于机器编号、IP地址后几位、启动时间(Environment.TickCount)等等。

这样,到这里Random的随机性问题终于可以告一段落了。

C# Random类的正确应用的更多相关文章

  1. 【Unity】6.6 Random类

    分类:Unity.C#.VS2015 创建日期:2016-04-20 一.简介 Unity引擎提供的Random类可以用来生成随机数.随机点或旋转角度. 1.成员变量 seed:设置用于随机数生成器的 ...

  2. Scanner类、匿名对象、Random类、ArrayList集合、String类、static静态类、math类和Arrays工具类

    一.Scanner类 1.除了八种基本数据类型,其他都是引用类型: 引用类型使用三步骤: 2.Scanner类 引用jdk提供的类,Scanner在java.util包下,不在java.lang包(S ...

  3. .Net使用system.Security.Cryptography.RNGCryptoServiceProvider类与System.Random类生成随机数

    .Net中我们通常使用Random类生成随机数,在一些场景下,我却发现Random生成的随机数并不可靠,在下面的例子中我们通过循环随机生成10个随机数: ; i < ; i++) { Rando ...

  4. System类和Random类

    System类 成员方法: public static void gc():运行垃圾回收器 public static void exit(int status):退出垃圾回收器 public sta ...

  5. ActionScript 3.0 自写类整理笔记(十三)——Random类

    一个简单的随机函数工具类,总共提供了9种静态方法来获取不同的随机值随便写的,如果你还有什么更好的建议,请提出来,谢谢~ index.Random类:代码:public final class Rand ...

  6. Java api 入门教程 之 JAVA的Random类

    在实际的项目开发过程中,经常需要产生一些随机数值,例如网站登录中的校验数字等,或者需要以一定的几率实现某种效果,例如游戏程序中的物品掉落等. 在Java API中,在java.util包中专门提供了一 ...

  7. Random类

    Random类是随机数产生类,可以指定一个随机数的范围,然后任意产生在此范围中的数字. //================================================= // F ...

  8. JAVA的Random类[转]

    在实际的项目开发过程中,经常需要产生一些随机数值,例如网站登录中的校验数字等,或者需要以一定的几率实现某种效果,例如游戏程序中的物品掉落等. 在Java API中,在java.util包中专门提供了一 ...

  9. Math类和Random类(数学公式相关类)

    Math 类包含用于执行基本数学运算的方法,如初等指数.对数.平方根和三角函数. 常用方法: 1.static 数值类型 abs(数值类型 a)      返回 double 值的绝对值. 2.sta ...

随机推荐

  1. 理解 Android Binder 机制(一):驱动篇

    Binder的实现是比较复杂的,想要完全弄明白是怎么一回事,并不是一件容易的事情. 这里面牵涉到好几个层次,每一层都有一些模块和机制需要理解.这部分内容预计会分为三篇文章来讲解.本文是第一篇,首先会对 ...

  2. LruCache缓存bitmap(三)

    应用在网络连接上,onrestart后不会重新联网获取图片,省去了流量, public class MainActivity extends AppCompatActivity { ImageView ...

  3. [OGeek2019]bookmanager

    做过的代码量最大的一个题 说出的好也好,不好也不好,利用点很简单,就是一个大规模的heapoverflow,就是逆起来有点儿难度 思路很简单,就是利用堆溢出覆盖结构体中的指针为__free_hook, ...

  4. Azure Cosmos DB (四) 使用EF的SQL API 异地冗余

    一,引言 上一篇文章中,我们介绍到使用了EF Core 与Cosmos DB SQL API 进行结合开发.同时,大家在开发过程中一定要记得EF Core 不支持Cosmos DB 的迁移.今天我们启 ...

  5. DOM4J API

    1.DOM4J简介 DOM4J是 dom4j.org 出品的一个开源 XML 解析包.DOM4J应用于 Java 平台,采用了 Java 集合框架并完全支持 DOM,SAX 和 JAXP. DOM4J ...

  6. 三分钟带你分清Mysql 和Oracle之间的误区

    摘要:Mysql 和Oracle,别再傻傻分不清. mysql 和Oracle 在开发中的使用是随处可见的,那就简单去了解一下这俩款火的不行的数据库. 本质区别: Oracle数据库是一个对象关系数据 ...

  7. 企业微信公众号告警Python脚本

    #!/usr/bin/env python # -*- coding: utf-8 -*- import time import requests import json import os impo ...

  8. jupyter IPthon常用命令

    Jupyter IPython 一.安装Anaconda 到官网下载Anaconda安装包 双击Anaconda3-5.0.0-Windows-x86_64.exe文件 会出现如下的页面 1. 点击i ...

  9. MySQL主主数据同步

    环境 操作系统版本:CentOS 6.5 64位MySQL版本:mysql5.6节点1IP:192.168.0.235 主机名:taojiang1-mysql-01节点2IP:192.168.0.23 ...

  10. 全排列算法--递归实现(Java)

    求一个n阶行列式,一个比较简单的方法就是使用全排列的方法,那么简述以下全排列算法的递归实现. 首先举一个简单的例子说明算法的原理,既然是递归,首先说明一下出口条件.以[1, 2]为例 首先展示一下主要 ...