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. 微服务nacos服务注册与发现

    一,以上一篇为基础 微服务从nacos配置中心获得配置信息 给service1, service2添加依赖 <dependency> <groupId>com.alibaba. ...

  2. vivo 基于原生 RabbitMQ 的高可用架构实践

    一.背景说明 vivo 在 2016 年引入 RabbitMQ,基于开源 RabbitMQ 进行扩展,向业务提供消息中间件服务. 2016~2018年,所有业务均使用一个集群,随着业务规模的增长,集群 ...

  3. java安全编码指南之:文件IO操作

    目录 简介 创建文件的时候指定合适的权限 注意检查文件操作的返回值 删除使用过后的临时文件 释放不再被使用的资源 注意Buffer的安全性 注意 Process 的标准输入输出 InputStream ...

  4. matplotlib中plt用法实例

    import torch from models.models import Model import cv2 from PIL import Image import numpy as np fro ...

  5. Gym102012A Rikka with Minimum Spanning Trees

    题意 \(T\) 组数据,每组数据给定一个 \(n\) 个点,\(m\) 条边,可能含有重边自环的图,求出最小生成树的个数与边权和的乘积,对 \(10^9+7\) 取模. \(\texttt{Data ...

  6. Redis事务使用方法

    Redis事务 Redis事务是一组命令的集合,也是Redis的最小执行单位之一.一个事务的所有命令,要么都执行,要么都不执行.Redis能保证事务执行期间不会有其他命令插入. 相关命令 命令 格式 ...

  7. Java才是世界上最好的语言,Java在高频交易中替代C++

    高频交易 高频交易是指从那些人们无法利用的极为短暂的市场变化中寻求获利的计算机化交易,比如,某种证券买入价和卖出价差价的微小变化,或者某只股票在不同交易所之间的微小价差.在高频交易中,自动化应用程序每 ...

  8. .net 实现签名验签

    本人被要求实现.net的签名验签,还是个.net菜鸡,来分享下采坑过程 依然,签名验签使用的证书格式依然是pem,有关使用openssl将.p12和der转pem的命令请转到php实现签名验签 .ne ...

  9. 【kata Daily 190905】What's a Perfect Power anyway?(完美幂)

    原题: A perfect power is a classification of positive integers: In mathematics, a perfect power is a p ...

  10. 最简单的基于FFmpeg的直播系统开发移动端例子:IOS 视频解码器

    本文记录IOS平台下基于FFmpeg的视频解码器.该示例C语言的源代码来自于<最简单的基于FFMPEG+SDL的视频播放器>.相关的概念就不再重复记录了. 源代码 项目的目录结构如图所示. ...