c# - ConcurrentDictionary线程安全替代方案中的AddOrUpdate方法

标签 c# multithreading concurrentdictionary

按照此页面“备注”部分中的Is AddOrUpdate thread safe in ConcurrentDictionary?链接https://msdn.microsoft.com/en-us/library/dd287191(v=vs.110).aspx。它说

"However, delegates for these methods are called outside the locks to avoid the problems that can arise from executing unknown code under a lock. Therefore, the code executed by these delegates is not subject to the atomicity of the operation."


问题是我在我的代码中广泛使用了该代码,并意识到我有一个潜在的非常讨厌的错误(我的理解一直以来都是错误的)...因为在使用下面的方式时,我没想到可以在同时覆盖内部字典:
internal class HandlerProducers
{
    readonly ConcurrentDictionary<Type, Dictionary<Type, InstanceProducer>> handlerProducers
        = new ConcurrentDictionary<Type, Dictionary<Type, InstanceProducer>>();

    public void AddProducer(Type notificationType, Type concreteType, InstanceProducer producer)
    {
        this.handlerProducers
                .AddOrUpdate(
                    notificationType,
                    (key) => new Dictionary<Type, InstanceProducer> { { concreteType, producer } },
                    (key, dictionary) => { dictionary.Add(concreteType, producer); return dictionary; });
    }

    public IEnumerable<InstanceProducer> GetForHandler(Type notificationHandlerType)
    {
        if(this.handlerProducers.TryGetValue(notificationHandlerType, out var dict))
        {
            foreach (var kvp in dict)
                yield return kvp.Value;
        }
    }
}
我还有一个挑战,就是天真地将锁置于适当位置可能会引起热点,以上类广泛用于通过GetForHandler()进行读取,并且有时会被写入(通过AddProducer()方法)。
通过具有大量读取和偶发写入的性能设计人员,确保这是线程安全的最佳方法是什么?

最佳答案

我建议使用嵌套的ConcurrentDictionary结构:

readonly ConcurrentDictionary<Type, ConcurrentDictionary<Type, InstanceProducer>> handlerProducers
    = new ConcurrentDictionary<Type, ConcurrentDictionary<Type, InstanceProducer>>();
然后,您可以像这样实现AddProducer方法:
public void AddProducer(Type notificationType, Type concreteType,
    InstanceProducer producer)
{
    ConcurrentDictionary<Type, InstanceProducer> innerDict =
        this.handlerProducers.GetOrAdd(notificationType,
            _ => new ConcurrentDictionary<Type, InstanceProducer>());

    if (!innerDict.TryAdd(concreteType, producer))
        throw new InvalidOperationException(
            $"{notificationType}.{concreteType} already exists.");
}
方法GetForHandler无需修改。
此实现将使HandlerProducers类真正具有线程安全性,而不会牺牲其效率。

关于c# - ConcurrentDictionary线程安全替代方案中的AddOrUpdate方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63314627/

相关文章:

c# - 在 C# 中获取网络接口(interface)名称

c# - 检索准确日期的最佳方法?

c# - 为什么这个例子中的队列锁是必要的

c# - ConcurrentDictionary.GetOrAdd - 只有在不为空时才添加

c# - 等待所有线程完成写入 ConcurrentDictionary

C# Regex "Verbose"就像在 Python 中一样

c# - 在 C# 中管理一棵人造树

java - 如何在循环中更改 JLabel 的文本而不切换得太快以至于用户看不到它?

使用 join() 的 Java 多线程程序在计算相邻数字之和时给出了错误的结果

c# - ConcurrentDictionary 和原子操作——有时需要 Lock 吗?