我有一个消费者类是 Singleton 。是否有可能以任何方式 有条件地 锁定资源。目前,外部资源是"全球" 与AsyncEx(https://github.com/StephenCleary/AsyncEx)框架锁定。但我更愿意根据输入消息中的键锁定它,因此具有不同键的消息不应锁定资源。
public class Consumer
{
private readonly AsyncLock _mutex = new AsyncLock();
protected async Task Process(Message msg)
{
**UNBLOCKED CODE**
using (await _mutex.LockAsync())
{
await GetData(msg.Key);
}
**UNBLOCKED CODE**
}
}
有什么建议吗?
答案 0 :(得分:3)
不要只有一个AsyncLock,而是创建一个充满它们的字典。消息键是字典中AsyncLock的键。只要一次只有一条消息具有给定的密钥处理,就不会有任何阻塞。我进一步假设你的消息密钥是一个字符串。
private readonly Dictionary<string, AsyncLock> _mutexes = new Dictionary<string, AsyncLock>();
private AsyncLock GetMutex(string key)
{
lock (_mutexes)
{
AsyncLock mutex;
if (!_mutexes.TryGetValue(key, out mutex))
{
// no mutex yet, create a new one
mutex = new AsyncLock();
_mutexes.Add(key, mutex);
}
return mutex;
}
}
....
protected async Task Process(Message msg)
{
using (await GetMutex(msg.Key).LockAsync())
{
...
}
}
请注意,如果密钥的互斥锁已经存在,则很可能进一步优化GetMutex方法以避免锁定操作,例如:首先检查ContainsKey,如果没有,则锁定并添加。但是我不确定这种优化是否是100%线程安全的,所以我没有在这里包含它。
受到Jason的ConcurrentDictionary评论的启发,我决定提供GetMutex的ConcurrentDictionary版本的代码。我不建议尝试上面提到的关于优化对非线程安全字典的访问的内容。
ConcurrentDictionary<string, AsyncLock> _mutexes = new ConcurrentDictionary<string, AsyncLock>();
private AsyncLock GetMutexes(string key)
{
return _mutexes.GetOrAdd(key, s => { return new AsyncLock(); });
}