有条件地锁定资源

时间:2016-03-23 19:42:17

标签: c# asynchronous parallel-processing

我有一个消费者类是 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**
    }
}

有什么建议吗?

1 个答案:

答案 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(); });
}