我已经阅读了很多关于双重检查锁定的危险,我会努力远离它,但据说说我认为它们是一个非常有趣的阅读.
我正在阅读Joe Duffy关于使用双重检查锁定实现单例的这篇文章:http: //www.bluebytesoftware.com/blog/PermaLink,guid,543d89ad-8d57-4a51-b7c9-a821e3992bf6.aspx
而他似乎提出的(变体)解决方案是这样的:
class Singleton {
private static object slock = new object();
private static Singleton instance;
private static int initialized;
private Singleton() {}
public Instance {
get {
if (Thread.VolatileRead(ref initialized) == 0) {
lock (slock) {
if (initialized == 0) {
instance = new Singleton();
initialized = 1;
}
}
}
return instance;
}
}
Run Code Online (Sandbox Code Playgroud)
}
我的问题是,是否仍然存在写入重新排序的危险?具体来说这两行:
instance = new Singleton();
initialized = 1;
Run Code Online (Sandbox Code Playgroud)
如果这些写入被反转,那么其他一些线程仍然可以读取null.
今天,我在.NET Core中进行了一些测试,并且遇到了一些有趣的事情。
以前(〜.NET Framework 4)Random使用过Environment.TickCount,但现在我相信情况已经改变。
考虑以下代码:
while (true)
{
Random random = new Random();
Console.WriteLine(random.Next(0, 10000));
}
Run Code Online (Sandbox Code Playgroud)
在较早的.NET Framework版本中,new Random()空构造函数将使用Environment.TickCount,这将导致伪随机值的重复。
因此,您可以期待如下结果:
542
4211
5244
5244
5244
9501
9501
Run Code Online (Sandbox Code Playgroud)
以此类推。
在使用最新编译器的最新.NET Core版本上,我收到以下结果:
5332
220
3928
524
2973
2840
4965
5667
657
6434
3170
3046
7044
Run Code Online (Sandbox Code Playgroud)
绝对可以改善。
在旧版本中,其他表现出此问题的SO问题:
我的设置:.NET Core 2.2 /最新的C#编译器。
实际问题
所以我的问题是,PRNG是否真的得到了改善,或者他们只是将构造函数更改为使用其他默认种子,如果是,则它们用作种子是什么?密码术现在是否更安全(如果他们实际上改变了实现)?
我的代码中有一个循环
Parallel.For(0, Cnts.MosqPopulation, i => { DoWork() });
Run Code Online (Sandbox Code Playgroud)
但是在DoWork()函数中,有多个对随机数生成器的调用,其定义如下:
public static class Utils
{
public static readonly Random random = new Random();
}
Run Code Online (Sandbox Code Playgroud)
它是静态实例,因此只播种一次。我可以在整个代码中使用它。
根据 MSDN 和其他 stackoverflow 线程,这不是线程安全的。事实上,我注意到有时我的代码会中断并且随机数生成器开始生成全零(根据 MSDN 文档)。
还有其他的 stackoverflow 线程,但是比较老,而且实现速度很慢。我不能在生成数字上浪费时间,因为该程序是一项科学计算,并且正在运行数百次模拟。
我从 2.0 天开始就没有使用过 .net,我不确定该语言是如何演变成能够制作快速、高效、线程安全的 RNG。
以下是之前的主题:
注意:因为我需要一个快速的实现,所以我不能使用RNGCryptoServiceProvider相当慢的。
注2:我没有最少的工作代码。我什至不知道从哪里开始,因为我不知道线程安全是如何工作的,也不知道 C# 的高级知识。所以看起来我确实在要求一个完整的解决方案。
所以正如在 tile 中所说,我正在尝试使用 16 个(在我的情况下)线程用随机数填充一个字节数组,现在使用一个线程填充一个包含 500000000 个字节的数组大约需要六秒半的时间,所以逻辑上说,使用 16 个线程至少会快 10 倍,但后来我尝试这样做,花了 15 秒来填充它,我所做的是给每个线程一个段来填充相同的数组
这是代码:
static byte[] nums2 = new byte[500000000];
static Random rnd = new Random(123);
static void fill()
{
for (int i = 0; i < nums.Length; i++)
nums[i] = (byte)rnd.Next(10);
}
static void fillPart(object ID)
{
var part = nums2.Length / Environment.ProcessorCount;
int baseN = (int)ID * part;
for (int i = baseN; i < baseN + part; i++)
nums2[i] = (byte)rnd.Next(10);
Console.WriteLine("Done! " + ID);
}
static …Run Code Online (Sandbox Code Playgroud) 我正在阅读本节,最后一段说明示例代码不是线程安全的.我的问题是:这不会有助于增加其随机性(即如果多个线程同时执行这些行)?
注意到在多线程上并行生成随机数时,加密随机数生成器不是线程安全的.使用的生成器RNGCryptoServiceProvider似乎重复了很长的随机位(128位).重现此代码的代码如下所示.
如果没有使用锁来保护对RNGCryptoServiceProvider实例的访问(这会杀死整个速度点),有没有人有更快的方法来生成加密随机数?
using System;
using System.Runtime.Caching;
using System.Security.Cryptography;
using System.Threading.Tasks;
namespace ParallelRandomness
{
class Program
{
static void Main(string[] args)
{
var test = new Test();
Console.Write("Serialized verion running ... ");
test.Run(false);
Console.WriteLine();
Console.Write("Parallelized verion running ... ");
test.Run(true);
Console.WriteLine(Environment.NewLine + "Done.");
Console.ReadLine();
}
}
class Test
{
private readonly RNGCryptoServiceProvider _rng = new RNGCryptoServiceProvider();
private readonly byte[] _randomBytes = new byte[128 / 8];
private int collisionCount = 0;
private readonly object collisionCountLock = new object(); …Run Code Online (Sandbox Code Playgroud) 尝试找到并理解在 .NET Core 2.x 或更高版本中实现线程安全数字生成器的最佳方法
读完后,我觉得有几个“好”的方法 -
如果需要强大的加密随机性,基本上您会选择后者。
经过一些额外的研究,我发现由于 .NET Core 2.x System.Random 类被修改,因此默认种子生成不再主要依赖于系统计时器。(https://blogs.siliconorchid.com/post/coding-inspiration/randomness-in-dotnet)
问题 - 这对线程安全随机类的实现有何影响?
引用第一个链接 Iv'e 共享代码解决方案 -
public static class RandomGen2
{
private static Random _global = new Random();
[ThreadStatic]
private static Random _local;
public static int Next()
{
Random inst = _local;
if (inst == null)
{
int seed;
lock (_global) seed = _global.Next();
_local = inst = new Random(seed);
}
return inst.Next(); …Run Code Online (Sandbox Code Playgroud) 我一直在使用中发现的OAuthBase类这里我SSIS 2008 C#脚本组件(.NET 3.5).
它一直运行良好,但最近我遇到了问题,如果我在同一个数据流任务中执行多个脚本组件,使用上面的OAuthBase类中的GenerateNonce方法,我最终得到相同的随机数(随机数).
以下是生成nonce的OAuthBase类的摘录:
using System;
using System.Security.Cryptography;
using System.Collections.Generic;
using System.Text;
using System.Web;
namespace OAuth {
public class OAuthBase {
....snip......
protected Random random = new Random();
public virtual string GenerateNonce() {
// Just a simple implementation of a random number between 123400 and 9999999
return random.Next(123400, 9999999).ToString();
}
}
}
Run Code Online (Sandbox Code Playgroud)
在每个脚本组件中,我使用此C#代码来启动类并生成一个nonce:
OAuthBase oAuth = new OAuthBase();
string nonce = oAuth.GenerateNonce();
Run Code Online (Sandbox Code Playgroud)
从我的搜索周围,我认为这与它不是线程安全有关?我不完全确定.
我只能在SSIS 2008中运行.NET 3.5,所以我知道在.NET 4.0中引入的一些我不能使用的新东西.
关于如何修改OAuthBase类和/或我的C#脚本组件代码的任何想法?