c# - 我如何告诉 `ConcurrentDictionary.GetOrAdd` 不添加值?

标签 c# concurrentdictionary

我有几个使用 ConcurrentDictionary<TKey, TValue> 的案例用于缓存值,但通常我需要执行值验证以决定是否使用 ConcurrentDictionary<TKey, TValue>.GetOrAdd(TKey, Func<TKey, TValue>) 将其添加到缓存中.

通常沿着:

private readonly ConcurrentDictionary<Type, ISomeObject> someObjectCache = 
    new ConcurrentDictionary<Type, ISomeObject>();
public ISomeObject CreateSomeObject(Type someType)
{
    return someObjectCache.GetOrAdd(someType, type =>
    {
        if(!Attribute.IsDefined(someType, typeof(SomeAttribute))
            // Do something here to avoid the instance from being added to
            //    `someObjectCache`

        ISomeObject someObject;
        // Typical factory functionality goes here
        return someObject;
    });
}

我今天处理这个问题的方法是抛出一个看起来工作正常的异常,但我想要一个更简洁的方法(也许我可以设置一个标志或者我可以将返回值设置为一个特定值)来取消GetOrAdd来自 lambda 内部(尽管它实际上可以被一个成熟的方法所取代)。

根据我使用其他类似 LINQ 的方法的经验,返回 null将导致在不进行检查的情况下添加值(并且读取 GetOrAdd 的 IL 看起来会导致相同的问题),所以我认为这行不通。

有什么方法可以避免使用异常来取消使用 GetOrAdd 的添加吗? ?

最佳答案

根据我的阅读,有 no guarantee that the Add factory method will only be called a single time amongst all callers to Get for the same key .

该页面的相关部分在底部,引用如下:

Also, although all methods of ConcurrentDictionary(Of TKey, TValue) are thread-safe, not all methods are atomic, specifically GetOrAdd and AddOrUpdate. The user delegate that is passed to these methods is invoked outside of the dictionary's internal lock. (This is done to prevent unknown code from blocking all threads.) Therefore it is possible for this sequence of events to occur:

1) threadA calls GetOrAdd, finds no item and creates a new item to Add by invoking the valueFactory delegate.

2) threadB calls GetOrAdd concurrently, its valueFactory delegate is invoked and it arrives at the internal lock before threadA, and so its new key-value pair is added to the dictionary.

3) threadA's user delegate completes, and the thread arrives at the lock, but now sees that the item exists already

4) threadA performs a "Get", and returns the data that was previously added by threadB.

Therefore, it is not guaranteed that the data that is returned by GetOrAdd is the same data that was created by the thread's valueFactory. A similar sequence of events can occur when AddOrUpdate is called.

我的理解是,即使您在 Add 委托(delegate)中调用一些锁定,您也不能保证从 add 返回的值就是将实际使用的值。

因此,您不需要添加任何进一步的锁定,而是可以使用以下模式:

private ConcurrentDictionary<Type, ISomeObject> someObjectCache = 
    new ConcurrentDictionary<Type, ISomeObject>();
public ISomeObject CreateSomeObject(Type someType)
{

    ISomeObject someObject; 
    if (someObjectCache.TryGet(someType, out someObject))
    {
       return someObject;
    }

    if (Attribute.IsDefined(someType, typeof(SomeAttribute)) 
    { 
        // init someObject here
        someObject = new SomeObject(); 

        return someObjectCache.GetOrAdd(someType, someObject); // If another thread got through here first, we'll return their object here. 
    }

    // fallback functionality goes here if it doesn't have your attribute. 
}

是的,这将导致可能多次创建新对象,但调用者都会收到相同的结果,即使调用了多个对象也是如此。与 GetOrAdd 现在所做的一样。

关于c# - 我如何告诉 `ConcurrentDictionary.GetOrAdd` 不添加值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9900814/

相关文章:

c# - Microsoft Office Interop Word 读取页眉和脚注

c# - 服务器端调用按钮的点击事件

c# - 枚举 ConcurrentDictionary 时是否可能遗漏初始项?

c# - 在每个线程中获取下一个值的正确方法是什么?

c# - 在 Mono 中检测符号链接(symbolic link)和管道

c# - C# 中 MySQL 二进制文件 (16) 的替代方案

c# - 如何使 Style 通用以便我可以在我的整个应用程序中使用它?

c# - 从 ConcurrentDictionary 中安全地删除列表映射

c# - 排序 ConcurrentDictionary 有意义吗?