由于该类是Disposable,我不知何故认为它应该可以用作单例。 我应该如何安全地使用它?
// Code uses Nuget package FluentAssertions
var key = "supersecret";
var keybytes = Encoding.UTF8.GetBytes(key);
var hmac = new HMACSHA256(keybytes);
var tokenBytes = Encoding.UTF8.GetBytes("tokentocompute");
var expected = hmac.ComputeHash(tokenBytes);
await Task.WhenAll(
Enumerable.Range(0, 100).Select(i => Task.Run(() =>
{
var hash = hmac.ComputeHash(tokenBytes);
// This throws most of the time
hash.ShouldBeEquivalentTo(expected, $"{i}");
}))
);
我认为它不是HMACSHA1.ComputeHash() thread-safety question的副本,因为它专门讨论设置密钥的不同线程,而我在每次调用时都使用相同的密钥。重读之后,它可能是重复的。会等你们的意见。
答案 0 :(得分:2)
来自MSDN:
此类型的任何公共静态(在Visual Basic中为Shared)成员都是线程安全的。不保证任何实例成员都是线程安全的。
即使这个段落出现在MSDN的每个类中,你也需要牢记这一点。
查看反编译的代码,它似乎在这里和那里使用了几个私有变量。由于它没有锁定,因此错误很快就会发生。
[HashAlgorithm.cs]
/// <summary>Represents the size, in bits, of the computed hash code.</summary>
protected int HashSizeValue;
/// <summary>Represents the value of the computed hash code.</summary>
protected internal byte[] HashValue;
/// <summary>Represents the state of the hash computation.</summary>
protected int State;
[...]
[HashAlgorithm.cs]
public byte[] ComputeHash(byte[] buffer)
{
if (this.m_bDisposed)
throw new ObjectDisposedException((string) null);
if (buffer == null)
throw new ArgumentNullException(nameof (buffer));
this.HashCore(buffer, 0, buffer.Length);
this.HashValue = this.HashFinal();
byte[] numArray = (byte[]) this.HashValue.Clone();
this.Initialize();
return numArray;
}
我们最终在我们的代码中放置了一个using-block,每次重新创建hmac实例。性能类似于在我们的粗略测试中围绕它进行全局锁定。我们希望避免使用例如过度设计的东西。由于性能相当不错,所以会发生线索。
await Task.WhenAll(
Enumerable.Range(0, 100).Select(i => Task.Run(() =>
{
byte[] hash;
using (var hma = new HMACSHA256(keybytes))
{
hash = hma.ComputeHash(tokenBytes);
}
//lock (this)
//{
// hash = hmac.ComputeHash(tokenBytes);
//}
// Both ways achieved the desired results and performance was similar
hash.ShouldBeEquivalentTo(expected, $"{i}");
}))
);