在没有异常处理的方法中使用SemaphoreSlim

时间:2016-03-02 16:04:43

标签: c# multithreading locking async-await semaphore

目前,我正在努力实施SemaphoreSlim以锁定"" "份"一种必须是线程安全的方法。我的问题是,在没有异常处理过载的情况下实现它是非常困难的。因为在" lock"之前抛出异常时将被释放,它将永远留在那里。

以下是一个例子:

private SemaphoreSlim _syncLock = new SemaphoreSlim(1);
private IDictionary<string, string> dict = new Dictionary<string, string>();

public async Task ProcessSomeThing(string input)
{
    string someValue = await GetSomeValueFromAsyncMethod(input);
    await _syncLock.WaitAsync();
    dict.Add(input, someValue);
    _syncLock.Release();
}

如果输入具有多次相同的值,此方法将抛出异常,因为具有相同键的项将被添加两次到字典并且&#34; lock&#34;将不会被释放。

假设我有很多_syncLock.Release();_syncLock.Release();,很难写出try-catch.ContainsKey或其他一些东西。这会完全破坏代码......当Exception被抛出或某个术语被抛弃时,是否可以释放锁?

希望我很清楚我要求/寻求什么。

谢谢大家!

2 个答案:

答案 0 :(得分:2)

您可以使用lock,因为受保护区域内没有await。处理所有这些。

如果情况并非如此,您可能需要在任何地方使用try-finally或编写自定义的一次性用户,以便您可以使用using的范围性质。

答案 1 :(得分:2)

我建议使用lockSemaphoreSlim。相反,使用正确的工具来完成工作 - 在这种情况下,使用ConcurrentDictionary<TKey, Lazy<TValue>>而不是使用IDictionary<string, string>以及锁定和信号量似乎是合适的。今年有几篇关于这种模式的文章here's one of the them。所以遵循这个建议的模式看起来像这样:

private ConcurrentDictionary<string, Lazy<Task<string>>> dict = 
    new ConcurrentDictionary<string, Lazy<Task<string>>>();

public Task ProcessSomeThing(string input)
{
    return dict.AddOrUpdate(
        input, 
        key => new Lazy<Task<string>>(() => 
            GetSomeValueFromAsyncMethod(key), 
            LazyThreadSafetyMode.ExecutionAndPublication),
        (key, existingValue) => new Lazy<Task<string>>(() => 
            GetSomeValueFromAsyncMethod(key), // unless you want the old value
            LazyThreadSafetyMode.ExecutionAndPublication)).Value;
}

最终,这为asynchronously添加dictionary实现了线程安全的目标。假设try / catch函数中存在GetSomeValueFromAsyncMethod,错误处理就会按照您的预期进行。还有一些资源:

最后,我创建了an example .NET fiddle to help demonstrate the idea