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. vue渐进式开发的理解和指令

    1.vue渐进式开发 vue是一个渐进式的框架,轻量,易于上手,为啥是渐进式那,我当时也很蒙,比如的官网是jquery写的,就可以通过script标签引入事先准备好的vue.min.js的压缩源代码或 ...

  2. 【Azure DevOps系列】Azure DevOps使用Docker将.NET应用程序部署在云服务器

    Docker持续集成 本章我们要实现的是通过我们往代码仓库push代码后,我们将每次的push进行一次docker自动化打包发布到docker hub中,发布到之后我将进行部署环节,我们将通过ssh方 ...

  3. Monitor Ctrl-Break线程,有点坑

    Monitor Ctrl-Break线程这个在idea中特有的线程,你了解吗?这线程可能会在你调试的时候给你带来谜一样的结果,为什么呢?请看下面的例子: 首先我们先复习一下多线程的状态(因为这个问题是 ...

  4. MacOs/Liunx主机搭建windows平台双机调试环境

    0x00 前言 本文的主要试用对象是Mac OS/Linux用户,对于想调试windows内核相关的一些东西时,需要搭建双机调试环境的一些记录.另外对于本机是windows的用户也完全试用,windo ...

  5. 实现图像上像素点与实际位置的GPS对应

    作者有话说 这篇随笔是基于我自己完成的一个项目,这个项目虽然看起来较为简单,但是由于我本身不是学这个方向的,因此在做的过程中还是遇到了一些大大小小的问题.经过仔细研究并多次调试代码,终于把这个问题的原 ...

  6. NB-IoT的HARQ过程是怎么样的

    NB-IoT的HARQ是一种将前向纠错(Forward Error Correction,FEC)编码和ARQ相结合而形成的技术.HARQ的基本原理是缓存没有正确接收到的数据,并且将重传数据和原始数据 ...

  7. 基于gin的golang web开发:访问mysql数据库

    web开发基本都离不开访问数据库,在Gin中使用mysql数据库需要依赖mysql的驱动.直接使用驱动提供的API就要写很多样板代码.你可以找到很多扩展包这里介绍的是jmoiron/sqlx.另外还有 ...

  8. 如何制作一本《现代Javascript教程》EPUB电子书

    制作一本<现代Javascript教程>电子书学习使用 计划学习JavaScript的同学可以看过来,今天就推荐个学习JavaScript的免费教程. 教程文档来源于 https://zh ...

  9. -bash: bash_profile: command not found问题

    这个问题一般就是.bash_profile 文件内容错误,里面内容没加注释之内的,vi .bash_profile打开文件检查一下,然后:wq!保存退出 我的错误就是红圈处没有注释造成的

  10. JavaScript中.、[]与setAttribute()在设置属性上的区别

    .和[] javaScript.和[]既可以对所有js对象设置属性,但是对于DOM对象它设置的属性有些特殊.对于元素DOM标准属性,实现属性值的设置/更改;对于元素DOM非标准属性,仅在js中有效,在 ...