ThreadSafeRandom

//ThreadSafeRandom.cs
using System;
using System.Security.Cryptography;

using System.Threading;
namespace Utility
//namespace System.Threading
{
	public class ThreadSafeRandom : Random
	{
		// seed provider
		private static readonly RNGCryptoServiceProvider _global = new RNGCryptoServiceProvider();

		// provider of randomness
		private ThreadLocal<Random> _local = new ThreadLocal<Random>(() =>
		{
			// 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);
		}
	}
}

测试:

//Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using System.Collections.Concurrent;
using System.Diagnostics;

namespace Test
{
    class Program
    {
		static void Main(string[] args)
        {
            TestThreadSafeRandom();
        }

		private static void TestThreadSafeRandom()
		{
			//普通Random出现大量重复
			TestThreadSafeRandom0();

			//统计上看:普通Random不如ThreadSafeRandom稳定
			TestThreadSafeRandom1();
		}

		private static void TestThreadSafeRandom0()
		{
			int taskCount = 4;
			int randomCount = 100000;
			var sw = Stopwatch.StartNew();
			var normalRandomSameCount = GenerateAndStatSameCount(taskCount, randomCount, GenerateNormalRandoms);
			Console.WriteLine("Normal random same count:{0}, Consume time:{1}", normalRandomSameCount, sw.Elapsed.ToString());

			sw.Restart();
			var threadSafeRandomSameCount = GenerateAndStatSameCount(taskCount, randomCount, GenerateThreadSafeRandoms);
			Console.WriteLine("Thread safe random same count:{0}, Consume time:{1}", threadSafeRandomSameCount, sw.Elapsed.ToString());
		}
		private static int GenerateAndStatSameCount(int taskCount, int randomCount, Func<int, List<int>> generateRandoms)
		{
			
			Task<List<int>>[] tasks = new Task<List<int>>[taskCount];
			for (int i = 0, length = tasks.Length; i < length; i++)
			{
				tasks[i] = Task.Factory.StartNew(() =>
				{
					return generateRandoms(randomCount);
				});
			}

			Task<int> finalTask = Task.Factory.ContinueWhenAll(tasks, (ts) =>
			{
				int sameCount = 0;
				List<int>[] ls = new List<int>[ts.Length];
				for (int i = 0, length = ls.Length; i < length; i++)
				{
					ls[i] = ts[i].Result;
				}
				for (int i = 0, length = randomCount; i < length; i++)
				{
					if (IsSameValueAtPostion(ls, i))
					{
						sameCount++;
					}
				}
				return sameCount;
			});
			finalTask.Wait();
			return finalTask.Result;
		}
		private static bool IsSameValueAtPostion(List<int>[] ls, int pos)
		{
			bool result = true;
			for (int i = 0, length = ls.Length - 1; i < length; i++)
			{
				if (ls[i][pos] != ls[i + 1][pos])
				{
					result = false;
					break;
				}
			}
			return result;
		}
		private static List<int> GenerateNormalRandoms(int randomCount)
		{
			List<int> randoms = new List<int>(randomCount);
			for (int i = 0; i < randomCount; i++)
			{
				Random rand = new Random();
				randoms.Add(rand.Next());
			}
			return randoms;
		}
		private static List<int> GenerateThreadSafeRandoms(int randomCount)
		{
			List<int> randoms = new List<int>(randomCount);
			for (int i = 0; i < randomCount; i++)
			{
				Utility.ThreadSafeRandom rand = new Utility.ThreadSafeRandom();
				randoms.Add(rand.Next());
			}
			return randoms;
		}


        private static void TestThreadSafeRandom1()
		{
			int taskCount = 4;
			int randomCount = 100000;
			Tuple<int, int> randomRange = new Tuple<int, int>(0, 10);

			Random normalRandom = new Random();
			Random threadSafeRandom = new Utility.ThreadSafeRandom();
			ConcurrentBag<int> numberGeneratedByNormalRandomParallel = new ConcurrentBag<int>();
			ConcurrentBag<int> numberGeneratedByThreadSafeRandomParallel = new ConcurrentBag<int>();

			var sw = Stopwatch.StartNew();

			GenerateRandomNumbersTasks(taskCount, randomCount, randomRange.Item1, randomRange.Item2, normalRandom, numberGeneratedByNormalRandomParallel);
			Console.WriteLine("rand type:{0}", normalRandom.GetType().Name);
			StatEachRandomNumberPercentage(randomRange.Item1, randomRange.Item2, numberGeneratedByNormalRandomParallel);

			sw.Restart();
			GenerateRandomNumbersTasks(taskCount, randomCount, randomRange.Item1, randomRange.Item2, threadSafeRandom, numberGeneratedByThreadSafeRandomParallel);
			Console.WriteLine("rand type:{0}", threadSafeRandom.GetType().Name);
			StatEachRandomNumberPercentage(randomRange.Item1, randomRange.Item2, numberGeneratedByThreadSafeRandomParallel);
		}
		private static void StatEachRandomNumberPercentage(int minValue, int maxValue, ConcurrentBag<int> bag)
		{
			int count = 0;
			int total = bag.Count;
			var groups = bag.ToLookup(e => e, e => 1);
			for (int i = minValue; i < maxValue; i++)
			{
				var group = groups[i];
				count = 0;
				if (group != null)
				{
					count = group.Count();
				}
				Console.WriteLine("\t{0} percentage:{1}", i, count / (double)total);
			}
		}
		private static void GenerateRandomNumbersTasks(int taskCount, int randomCount, int minValue, int maxValue, Random rand, ConcurrentBag<int> bag)
		{
			int perCount = randomCount / taskCount;
			Task[] tasks = new Task[taskCount];
			int length = taskCount - 1;
			for (int i = 0; i < length; i++)
			{
				tasks[i] = Task.Factory.StartNew(() =>
					GenerateRandomNumbers(perCount, minValue, maxValue, rand, bag));
			}
			tasks[length] = Task.Factory.StartNew(() =>
				GenerateRandomNumbers(randomCount - perCount * length, minValue, maxValue, rand, bag));
			Task.WaitAll(tasks);
		}
		private static void GenerateRandomNumbers(int randomCount, int minValue, int maxValue, Random rand, ConcurrentBag<int> bag)
		{
			for (int i = 0; i < randomCount; i++)
			{
				int num = rand.Next(minValue, maxValue);
				bag.Add(num);
			}
			//Parallel.For(0, randomCount, index =>
			//{
			//	bag.Add(rand.Next(minValue, maxValue));
			//});
		}
    }
}

CPU:四核,8逻辑处理器(使用超线程技术(Inter Hyper-Threading Technology),每个物理内核提供两份架构状态,获得8个硬件线程)(听着高大上,还没有工作用的单纯四核快;上面代码测试时,开4个任务要比开8个任务快)

2组测试结果:

1组:

Normal random same count:0, Consume time:00:00:00.3157778
Thread safe random same count:0, Consume time:00:00:32.5139927
rand type:Random
        0 percentage:0.1206
        1 percentage:0.09818
        2 percentage:0.09882
        3 percentage:0.09959
        4 percentage:0.09695
        5 percentage:0.09931
        6 percentage:0.09772
        7 percentage:0.09733
        8 percentage:0.09627
        9 percentage:0.09523
rand type:ThreadSafeRandom
        0 percentage:0.09955
        1 percentage:0.10108
        2 percentage:0.10031
        3 percentage:0.1009
        4 percentage:0.1
        5 percentage:0.09961
        6 percentage:0.09932
        7 percentage:0.09972
        8 percentage:0.1
        9 percentage:0.09951

2组:

Normal random same count:25988, Consume time:00:00:00.2909146
Thread safe random same count:0, Consume time:00:00:32.5904478
rand type:Random
        0 percentage:0.69254
        1 percentage:0.03408
        2 percentage:0.03349
        3 percentage:0.03364
        4 percentage:0.03416
        5 percentage:0.03373
        6 percentage:0.03454
        7 percentage:0.03422
        8 percentage:0.03438
        9 percentage:0.03522
rand type:ThreadSafeRandom
        0 percentage:0.09986
        1 percentage:0.10144
        2 percentage:0.10077
        3 percentage:0.09944
        4 percentage:0.10002
        5 percentage:0.09889
        6 percentage:0.09934
        7 percentage:0.09961
        8 percentage:0.10047
        9 percentage:0.10016

从第二组结果可以看出,并行情况下,普通Random生成的随机数有很大的问题:

1.每个任务使用局部变量时,普通Random生成大量相同随机数,这和Random的随机种子有很大的关系;

2.每个任务共享变量时,普通Random生成的随机数分布有很大的问题,这是因为普通Random不是线程安全的,其内部是有状态的,并行情况下,就出现问题了。

共享变量时,加锁或许是一个方法,这里没有测试,后续可以测试下,推测效率上应该没有ThreadSafeRandom高

 

追加:

普通Random与ThreadSafeRandom的效率对比:

//Program.cs
//class Program
private static void TestThreadSafeRandom2()
{
	int taskCount = 4;
	int randomCount = 100000;
	Tuple<int, int> randomRange = new Tuple<int, int>(0, 10);

	Random normalRandom = new Random();
	Random threadSafeRandom = new Utility.ThreadSafeRandom();

	ConcurrentBag<int> numberGeneratedByNormalRandomParallel = new ConcurrentBag<int>();
	ConcurrentBag<int> numberGeneratedByThreadSafeRandomParallel = new ConcurrentBag<int>();

	var sw = Stopwatch.StartNew();
	GenerateRandomNumbersTasks(taskCount, randomCount, randomRange.Item1, randomRange.Item2, normalRandom, numberGeneratedByNormalRandomParallel, GenerateNormalRandomNumbers);
	Console.WriteLine("{0}:{1}", normalRandom.GetType().Name, sw.Elapsed.ToString());
	StatEachRandomNumberPercentage(randomRange.Item1, randomRange.Item2, numberGeneratedByNormalRandomParallel);

	sw.Restart();
	GenerateRandomNumbersTasks(taskCount, randomCount, randomRange.Item1, randomRange.Item2, normalRandom, numberGeneratedByThreadSafeRandomParallel, GenerateThreadSafeRandomNumbers);
	Console.WriteLine("{0}:{1}", threadSafeRandom.GetType().Name, sw.Elapsed.ToString());
	StatEachRandomNumberPercentage(randomRange.Item1, randomRange.Item2, numberGeneratedByThreadSafeRandomParallel);
}
private static void GenerateRandomNumbersTasks(int taskCount, int randomCount, int minValue, int maxValue, Random rand, ConcurrentBag<int> bag, Action<int, int, int, Random, ConcurrentBag<int>> generate)
{
	int perCount = randomCount / taskCount;
	Task[] tasks = new Task[taskCount];
	int length = taskCount - 1;
	for (int i = 0; i < length; i++)
	{
		tasks[i] = Task.Factory.StartNew(() =>
			generate(perCount, minValue, maxValue, rand, bag));
	}
	tasks[length] = Task.Factory.StartNew(() =>
		generate(randomCount - perCount * length, minValue, maxValue, rand, bag));
	Task.WaitAll(tasks);
}
private static void GenerateNormalRandomNumbers(int randomCount, int minValue, int maxValue, Random rand, ConcurrentBag<int> bag)
{
	int num;
	for (int i = 0; i < randomCount; i++)
	{
		lock (rand)
		{
			num = rand.Next(minValue, maxValue);
		}
		bag.Add(num);
	}
}
private static void GenerateThreadSafeRandomNumbers(int randomCount, int minValue, int maxValue, Random rand, ConcurrentBag<int> bag)
{
	int num;
	for (int i = 0; i < randomCount; i++)
	{
		num = rand.Next(minValue, maxValue);
		bag.Add(num);
	}
}

private static void TestThreadSafeRandom3()
{
	int randomCount = 100000;
	Tuple<int, int> randomRange = new Tuple<int, int>(0, 10);

	Random normalRandom = new Random();
	Random threadSafeRandom = new Utility.ThreadSafeRandom();

	int num;
	int minValue = randomRange.Item1;
	int maxValue = randomRange.Item2;
	var sw = Stopwatch.StartNew();
	for (int i = 0; i < randomCount; i++)
	{
		num = normalRandom.Next(minValue, maxValue);
	}
	Console.WriteLine("{0}:{1}", normalRandom.GetType().Name, sw.Elapsed.ToString());

	sw.Restart();
	for (int i = 0; i < randomCount; i++)
	{
		num = threadSafeRandom.Next(minValue, maxValue);
	}
	Console.WriteLine("{0}:{1}", threadSafeRandom.GetType().Name, sw.Elapsed.ToString());
}
//Program.cs
//Program.TestThreadSafeRandom
private static void TestThreadSafeRandom()
{
	//普通Random出现大量重复
	//TestThreadSafeRandom0();

	//统计上看:普通Random不如ThreadSafeRandom稳定
	//TestThreadSafeRandom1();

	//并行:普通Random+lock 不如 ThreadSafeRandom 快
	TestThreadSafeRandom2();

	//串行:普通Random 比 ThreadSafeRandom 快
	TestThreadSafeRandom3();
}

一组测试输出:

Random:00:00:00.0368598
        0 percentage:0.09868
        1 percentage:0.09849
        2 percentage:0.10021
        3 percentage:0.10048
        4 percentage:0.10172
        5 percentage:0.10031
        6 percentage:0.1011
        7 percentage:0.09857
        8 percentage:0.10067
        9 percentage:0.09977
ThreadSafeRandom:00:00:00.0058398
        0 percentage:0.09968
        1 percentage:0.09933
        2 percentage:0.09975
        3 percentage:0.09915
        4 percentage:0.1003
        5 percentage:0.09972
        6 percentage:0.10234
        7 percentage:0.10029
        8 percentage:0.09897
        9 percentage:0.10047
Random:00:00:00.0021268
ThreadSafeRandom:00:00:00.0055171

不难得出结论:并行情况下,普通Random需要加锁,这样效率上就不如ThreadSafeRandom了;串行情况下,没有加锁需求,普通Random是比ThreadSafeRandom快的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值