HashAlgorithm.ComputeHash() 是线程安全的吗?

hIp*_*pPy 4 c# hash multithreading

我无法得到这个问题的明确答案,所以这个问题。过去很少有 SO 帖子提到HashAlgorithmMSDN 文档中的实例不是线程安全的引用片段。

但是,当前的 MSDN 文档并没有这么说。令人惊讶的是,下面的代码在net3.1、net5.0上不会爆炸,但在net6.0上却会爆炸。所以,看起来它是线程安全的(也许),但也许 net6.0 有一个错误。

//<TargetFrameworks>net6.0;net5.0;netcoreapp3.1;net48</TargetFrameworks>
[Explicit]
[Test]
public void Bork_HashAlgorithm()
{
    const int iterations = 1_000_000;
    var bytes = Encoding.UTF8.GetBytes("the overtinkerer");
    using (var md5 = MD5.Create())
    {
        Parallel.For(0, iterations, (i, loop) =>
        {
            md5.ComputeHash(bytes);
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

异常消息:

SafeHandle 不能为空。(参数“pHandle”)

Ale*_*lex 13

不,它不是线程安全的。

我们在峰值负载下看到了相同的异常:SafeHandle cannot be null. (Parameter 'pHandle')MD5 提供程序、SHA1 和 SHA256 到处都出现类似的错误。

然而我们发现使用HashAlgorithma 的“单例”实例仍然是有益的lock,它仍然比每次创建单独的实例快 3 倍。

这是基准

方法 意思是 错误 标准差 0代 第一代 第二代 已分配
MD5重建 1,282.5 纳秒 726.26纳秒 39.81纳秒 0.0801 0.0286 0.0038 512B
MD5单例带锁 402.2纳秒 39.38纳秒 2.16纳秒 0.0610 - - 384 乙
MD5_哈希数据 467.7纳秒 33.26纳秒 1.82纳秒 0.0548 - - 344乙

这是代码:

static MD5 _md5 = MD5.Create(); // <-- one instance for all threads
public static byte[] MD5Hash(byte[] input)
{
    lock (_md5) // <-- use a lock
    {
        return _md5.ComputeHash(input);
    }
}
Run Code Online (Sandbox Code Playgroud)

更新:MD5_HashData是 .NET 5 中引入的新静态MD5.HashData方法 - 它并不比使用lock. 然而,MD5.HashData在高度并行性下表现出更好的性能,这是 BDN 无法模拟的(请参阅下面的评论)

UPDATE2:添加 SHA256 基准

方法 意思是 错误 标准差 0代 第一代 已分配
共享重建 2.211 我们 3.7097 我们 0.2033我们 0.1144 0.0534 736B
SHASingletonWithLock 1.231 我们 0.1349 微秒 0.0074我们 0.0954 - 608乙
SHA_HashData 1.296 我们 0.0737 微秒 0.0040我们 0.0877 - 552 乙