由于该类是一次性的,我以某种方式认为它可能应该用作单例。我应该如何安全地使用它?
// 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}");
}))
);
Run Code Online (Sandbox Code Playgroud)
我不认为它是HMACSHA1.ComputeHash() 线程安全问题的重复,因为它专门讨论设置密钥的不同线程,而我在每次调用中都使用相同的密钥。重读之后,它可能仍然是重复的。坐等各位大佬的意见。
来自 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;
}
Run Code Online (Sandbox Code Playgroud)
我们最终在我们的代码中放置了一个 using 块,每次都重新创建 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}");
}))
);
Run Code Online (Sandbox Code Playgroud)