博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
用线程安全随机数解决Random在多线程中随机性重复的问题
阅读量:6230 次
发布时间:2019-06-21

本文共 4324 字,大约阅读时间需要 14 分钟。

在.NET中,随机数一般是用Random来获取,但是当在多任务的并行化编程时,问题就出现了。因为Random是基于时间作为种子来生成伪随机数的,而如果程序在多核并行时,在同一时间内的多个核中取到的时间是一样的,这样一来,生成的伪随机数就有可能会有一样的。如果业务需求中需要不可重复的随机数,那么这后果将会相当严重,所以必须采取一种新的方式来获取线程安全的伪随机数。下面是摘自《.NET Parallel Extensions》中的一段关于线程安全随机数生成的类,也可参看。

public class ThreadSafeRandom : Random    {        //This is the seed provider        private static readonly RNGCryptoServiceProvider _global = new RNGCryptoServiceProvider();        //This is the provider of randomness.        //There is going to be one instance of Random per thread        //because it is  declared as ThreadLocal
private ThreadLocal
_local = new ThreadLocal
(() => { //This is the valueFactory function //This code will run for each thread to initialize each independent instance of Random. var buffer = new byte[4]; //Calls the GetBytes method for RNGCryptoServiceProvider because this class is thread-safe //for this usage. _global.GetBytes(buffer); //Return the new thread-local Random instance initialized with the generated seed. return new Random(BitConverter.ToInt32(buffer, 0)); }); public override int Next() { return _local.Value.Next(); } public override int Next(int maxValue) { return _local.Value.Next(maxValue); } public override int Next(int minValue, int maxValue) { return _local.Value.Next(minValue, maxValue); } public override double NextDouble() { return _local.Value.NextDouble(); } public override void NextBytes(byte[] buffer) { _local.Value.NextBytes(buffer); } }
这个类ThreadSafeRandom继承自Random,所以可以像Random一样使用。

这里边关键用到了几个技术点:

1、RNGCryptoServiceProvider的加密随机生成器,再用其中的强随机序列的方法GetBytes来实现随机。

2、使用ThreadLocal来懒惰初使化(Lazy-Initialize)随机数的实例。因为ThreadLocal是针对于每一个线程的线程安全类,是线程的本地存储形式。如果同一个线程多次初始化ThreadLocal,那么得到的实例将会是一样的。因为如果一个线程已经初始化了该实例之后( ThreadSafeRandom safeRandom = new ThreadSafeRandom()),该线程后面继续初始化(再次调用 ThreadSafeRandom safeRandom = new ThreadSafeRandom())是不会再初始化一次,而是会返回之前的实例(有点像单件模式)。不过,这也带来了另一个问题,如果就是要在线程中不断产生新的实例时,这种做法就变的不合适了,不悉采用变通或者其他做法。

下面是关于Random和ThreadSafeRandom测试的实例

using System;using System.Collections.Generic;using System.Diagnostics;using System.Linq;using System.Text;using System.Threading.Tasks;namespace Chapter11_Console{    public class RadomTest    {        #region Run Function        public static void Run()        {            Console.WriteLine("Started!");            var sw = Stopwatch.StartNew();            int normalRandomSameCount = randomSerial(generateNormalRadoms);            Console.WriteLine("Normal Random Same Count:{0}, Consume Time:{1}", normalRandomSameCount, sw.Elapsed.ToString());            sw.Restart();            int threadSafeRandomSameCount = randomSerial(generateThreadSafeRadoms);            Console.WriteLine("Thread Safe Random Same Count:{0}, Consume Time:{1}", threadSafeRandomSameCount, sw.Elapsed.ToString());            Console.WriteLine("Completed!");            Console.ReadLine();        }        private static int randomSerial(Func
> generateRadoms) { int randomCount = 100000; Task
>[] tasks = new Task
>[2]; for (int i = 0; i < tasks.Length; i++) { tasks[i] = Task.Factory.StartNew(() => { return generateRadoms(randomCount); }); } Task.WaitAll(tasks); int sameCount = 0; Task finalTask = Task.Factory.StartNew(() => { for (int i = 0; i < randomCount; i++) { if (tasks[0].Result[i] == tasks[1].Result[i]) { sameCount++; } } }); finalTask.Wait(); return sameCount; } private static List
generateNormalRadoms(int randomCount) { List
randoms = new List
(); for (int i = 0; i < randomCount; i++) { Random random = new Random(); randoms.Add(random.Next()); } return randoms; } private static List
generateThreadSafeRadoms(int randomCount) { List
randoms = new List
(); for (int i = 0; i < randomCount; i++) { ThreadSafeRandom safeRandom = new ThreadSafeRandom(); randoms.Add(safeRandom.Next()); } return randoms; } #endregion }}
运行结果如下图

多运行几次会发现,有的时候Normal Random Same Count也有为0的时候,有的时候会很小,有的时候会很大,具体多少是随机性的。

注意:

   1、测试时必须是多核的。以上程序是在双核上运行的,如果有四核或更多的核,可以将任务数加大。

   2、线程安全虽然确保了随机数的安全性,但是会消耗更多时间。

转载请注明出处

转载于:https://www.cnblogs.com/sparkleDai/p/7605067.html

你可能感兴趣的文章
Spring Boot开发WEB页面
查看>>
Eclipse快捷键大全
查看>>
px和em和rem的区别
查看>>
OSChina 周六乱弹 —— “我们”快被你们玩坏了
查看>>
OSChina 周四乱弹 ——00后让别人给自己网购女朋友
查看>>
OSChina 周六乱弹 ——程序员的情怀:贫贱不能移
查看>>
螺旋矩阵
查看>>
SQLserver From simple To Full backup model
查看>>
一个不错的图片
查看>>
win32学习07.Windows消息机制
查看>>
Spring中使用import整合多个配置文件
查看>>
简单工厂模式
查看>>
热门搜索和历史搜索的设计思想
查看>>
php cgi模式下获取执行文件的完整路径
查看>>
防SQL注入过滤器的实现
查看>>
Android在onCreate()中获得控件尺寸
查看>>
php设置虚拟目录
查看>>
计算机是如何做加法的?(4)——构建半加器的初步设想
查看>>
最近打算把string_h下面的函数都实现一遍
查看>>
farpoint合计列不参与排序实现方法
查看>>