在方法中使用信号量苗条,无需异常处理

本文关键字:异常处理 信号量 方法 | 更新日期: 2023-09-27 18:37:01

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

下面是一个示例:

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

如果输入多次具有相同的值,此方法将引发异常,因为具有相同键的项目将被添加到字典中两次,并且不会释放"锁"。

假设我有很多_syncLock.Release();_syncLock.Release();,很难写try-catch.ContainsKey或其他东西。这将完全炸毁代码...是否可以在抛出Exception或离开某个术语时始终释放锁?

希望清楚我在问/想要什么。

谢谢大家!

在方法中使用信号量苗条,无需异常处理

我建议不要使用lockSemaphoreSlim。相反,使用正确的工具来完成工作 - 在这种情况下,使用ConcurrentDictionary<TKey, Lazy<TValue>>而不是使用IDictionary<string, string>,锁定和信号量似乎是合适的。一年前有几篇关于这种模式的文章,这是其中之一。因此,遵循此建议的模式将如下所示:

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中。错误处理按预期进行,假设您的GetSomeValueFromAsyncMethod函数中存在try / catch。更多资源:

  • 为什么ConcurrentDictionary.GetOrAdd(key, valueFactory)允许两次调用valueFactory?
  • http://softwareblog.alcedo.com/post/2012/01/11/Sometimes-being-Lazy-is-a-good-thing.aspx

最后,我创建了一个示例 .NET 小提琴来帮助演示这个想法。

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

如果不是这种情况,您要么需要在任何地方使用try-finally,要么编写自定义一次性文件,以便您可以使用using的范围。