如Random number generator only generating one random number所述,每次需要另一个随机数时,创建System.Random
的新实例通常是不正确的,因为System.Random
是根据时钟播种的,因此多个在相同的tick中创建的实例将产生相同的随机数。因此,一种常见做法(至少在单线程应用程序中)是创建一个存储在静态字段中的Random
实例,用于生成所有随机数。
RNGCryptoServiceProvider
没有这个特殊的缺陷......但显然是costly to instantiate,因此再次建议存储和重用它的单个实例。
Org.BouncyCastle.Security.SecureRandom
怎么样?我是否同样需要存储和重用它的单个实例,或者每次我需要另一个随机数时,是否可以根据需要创建实例?
答案 0 :(得分:2)
我们可以再次(比如相关问题)查看源代码以得出一些结论(SecureRandom源代码供参考)。
构造函数中的所有工作都用于创建伪随机生成器:
private static DigestRandomGenerator CreatePrng(string digestName, bool autoSeed)
{
IDigest digest = DigestUtilities.GetDigest(digestName);
if (digest == null)
return null;
DigestRandomGenerator prng = new DigestRandomGenerator(digest);
if (autoSeed)
{
prng.AddSeedMaterial(NextCounterValue());
prng.AddSeedMaterial(GetNextBytes(Master, digest.GetDigestSize()));
}
return prng;
}
创建摘要(哈希)不需要任何费用(相对于其他工作)。例如,默认情况下使用的Sha256Digest
(使用空构造函数)只分配小byte[]
缓冲区。创建DigestRandomGenerator
本身也不需要任何费用(耦合小缓冲区)。主要工作在这里:
prng.AddSeedMaterial(GetNextBytes(Master, digest.GetDigestSize()));
它使用" master" RNG生成种子价值。完整.NET平台上的主RNG为RNGCryptoServiceProvider
(其中SecureRandom
存储在静态字段中并仅初始化一次)。因此,创建SecureRandom
时的所有工作都是为伪RNG创建加密随机种子。
我说,最好不要每次都创建新实例,至少对于小代(对于一两个NextInt()
调用),因为如果你为每一个创建新实例生成的数字 - 基本上是成本的两倍(一次为种子生成加密随机数,一次生成目标随机数)。因为(据我所知),SecureRandom
是线程安全的 - 没有太多理由不重用一个实例。
旁注 - 我不认为RNGCryptoServiceProvider
很难创建链接声明。它的构造函数如下:
public RNGCryptoServiceProvider()
: this((CspParameters) null)
{
}
[SecuritySafeCritical]
public RNGCryptoServiceProvider(CspParameters cspParams)
{
if (cspParams != null)
{
this.m_safeProvHandle = Utils.AcquireProvHandle(cspParams);
this.m_ownsHandle = true;
}
else
{
// we are interested in this path
this.m_safeProvHandle = Utils.StaticProvHandle;
this.m_ownsHandle = false;
}
}
因此,当您创建新实例(不提供csp)时 - 它会重用相同的Utils.StaticProvHandle
,因此它使用相同的"非托管" RNG提供商的实例。这反过来意味着创建新实例并重用相同的实例在性能上没有区别。也许在.NET的早期版本中,它不是这样,不确定。