如果GetOrAdd忙于检索具有相同密钥的值,它是否会等待?

时间:2014-02-05 08:04:22

标签: c# .net asynchronous parallel-processing task-parallel-library

考虑以下代码:

void DoSomething(int key)
{
    concurrentDictionary.GetOrAdd(key, (k)=>
        {
            //Do some expensive over network and database to retrieve value.
        });

考虑2个线程正在调用DoSomething(2)。同时他们会看到字典中没有Key==2的项目。考虑Thread1开始使用昂贵的算法来检索值2。

问题1: Thread2会等Thread1完成工作吗?或者只是尝试检索值本身,并在将其添加到字典时将其丢弃? (因为Thread1已添加此内容)

问题2:如果Thread2没有等待,那么避免多次运行这种昂贵算法的最佳解决方案是什么?

2 个答案:

答案 0 :(得分:6)

它在documentation中告诉你:

  

如果您在不同的线程上同时调用GetOrAdd,则可能会多次调用 addValueFactory ,但每次调用时可能不会将其键/值对添加到字典中。

对于问题2,我考虑从ConcurrentDictionary<int,Something>更改为ConcurrentDictionary<int,Lazy<Something>>,其中addValueFactory方法只构建指定{{3}的Lazy<Something>模式。然后,valueFactory

Lazy<Something>将进行昂贵的操作

答案 1 :(得分:2)

  

考虑2个线程正在调用DoSomething(2)。 同时他们   会看到字典中没有Key == 2的项目。

我不认为粗体的情景是可能的,这将是一场竞争条件。 check-for-a-key操作受GetOrAdd内的锁定机制保护。无论哪个线程首先获得锁定,都有机会添加键/值。另一个线程将等待阻塞,然后只有GetOrAdd释放第一个线程获取的锁,才会获得该值。

我错了。 answer by @Damien_The_Unbeliever是正确的。以下是GetOrAdd实现的样子。锁只发生在TryGetValueTryAddInternal内。我很想知道这种设计决策背后的背景。

public TValue GetOrAdd(TKey key, Func<TKey, TValue> valueFactory)
{
    TValue local;
    if (key == null)
    {
        throw new ArgumentNullException("key");
    }
    if (valueFactory == null)
    {
        throw new ArgumentNullException("valueFactory");
    }
    if (!this.TryGetValue(key, out local))
    {
        this.TryAddInternal(key, valueFactory(key), false, true, out local);
    }
    return local;
}