目前,我正在努力实施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
被抛出或某个术语被抛弃时,是否可以释放锁?
希望我很清楚我要求/寻求什么。
谢谢大家!
答案 0 :(得分:2)
您可以使用lock
,因为受保护区域内没有await
。处理所有这些。
如果情况并非如此,您可能需要在任何地方使用try-finally
或编写自定义的一次性用户,以便您可以使用using
的范围性质。
答案 1 :(得分:2)
我建议不使用lock
或SemaphoreSlim
。相反,使用正确的工具来完成工作 - 在这种情况下,使用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
,错误处理就会按照您的预期进行。还有一些资源: