【问题标题】:TryRemove a key-value pair from ConcurrentDictionary in C#尝试从 C# 中的 ConcurrentDictionary 中删除键值对
【发布时间】:2016-09-15 16:55:12
【问题描述】:

我的情况是我想要一个像这样在ConcurrentDictionary 上的方法。

bool TryRemove(TKey key, TValue value) {
    // remove the value IF the value passed in == dictionary[key]
    // return false if the key is not in the dictionary, or the value is not equal
}

有没有办法同时做到这一点?我正在努力寻找这种情况的答案,尽管这似乎是一个常见的用例。

我可以做这样的事情,但如果我已经在使用ConcurrentDictionary,我想避免锁定。我还必须锁定 GetOrAdd()AddOrUpdate() 其他地方的电话。似乎应该有更好的方法使用ConcurrentDictionary

ConcurrentDictionary<int, string> dict = ...;

/// stuff

int keyTryToRemove = 1337;
string valTryToRemove = "someValue";

bool success = false;
lock(keyTryToRemove) {
    string val;
    if (dict.TryRemove(keyTryToRemove, out val)) {
        if (val == valTryToRemove) {
            success = true;
        }
        else { // reinsert value, UGLY!
            dict[keyTryToRemove] = val;
            success = false;
        }
    } else {
        success = false;
    }
}

【问题讨论】:

  • 如果 TryGetValueRemove 返回 true 并且值匹配,该怎么办?不过你仍然需要锁。
  • @Igor 根据键删除并返回值,如果键和值都匹配,OP希望删除条目。他们甚至在那里尝试使用它。
  • @Igor 这正是我不想要的。我想有条件地删除该值,前提是它与我传递给它的值匹配。
  • @juharr - 啊,我错过了。

标签: c# concurrency concurrentdictionary


【解决方案1】:

由于ConcurrentDictionary&lt;TKey, TValue&gt; 类实现(虽然显式)IDictionary&lt;TKey, TValue&gt;,因此ICollection&lt;KeyValuePair&lt;TKey, TValue&gt;&gt;,您可以简单地将其转换为后者并使用Remove 方法,如下所示:

bool success = ((ICollection<KeyValuePair<TKey, TValue>>)dict).Remove(
    new KeyValuePair<TKey, TValue>(key, value));

implementation 在内部使用与公共 TryRemove 方法相同的线程安全方法(另外传递要检查的值) - 完全符合其应有的要求。

编辑: 一般而言,通过引入像这样的自定义扩展方法:

public static class Extensions
{
    public static bool TryRemove<TKey, TValue>(this ICollection<KeyValuePair<TKey, TValue>> source, TKey key, TValue value)
    {
        return source.Remove(new KeyValuePair<TKey, TValue>(key, value));
    }
}

这样示例代码就变得简单了:

bool success = dict.TryRemove(key, value);

【讨论】:

  • 哇,这很酷,而且我认为正是我要找的。一个问题,这仍然是线程安全的,对吧?
  • @JohnCarpenter - 是的,我刚刚查过了。
  • 它使用与公共TryRemove-source相同的内部实现
【解决方案2】:

我会做这样的事情

bool success = false;
lock(lockForDictionary) 
{
    string val;
    if (dict.TryGetValue(keyTryToRemove, out val) && val == valTryToRemove) 
    {
        dict.Remove(keyTryToRemove);
        success = true;
    } 
}

【讨论】:

  • 什么是lockForDictionary
  • 是的,这比我在回答中所做的要好得多。只能用钥匙锁吗?我认为是的,但我想验证是这样的
  • @JohnCarpenter 不,您希望您的锁用于特定的字典实例。我将其留给您确定如何实现,但我也不会将字典本身用作锁,而只是专门为锁定而创建的object
  • @JohnCarpenter 你将如何保证密钥是线程安全的?您永远不想将外部实体拥有的对象用作锁
【解决方案3】:

您可以在IDictionary 上编写类似这样的扩展方法。

public static class DictionaryHelper
{
    public static bool TryRemoveIfValue<TKey, TValue>(this IDictionary<TKey,TValue> owner, TKey key, TValue value)
    {
        var success = false;
        lock (owner)
        {
            TValue existingValue;
            if (owner.TryGetValue(key, out existingValue))
            {
                if (value.Equals(existingValue))
                {
                    owner.Remove(key);
                    success = true;
                }
            }
        }
        return success;
    }
}

【讨论】:

    【解决方案4】:

    下面是一段代码,可以稍微简化一下步骤。

    readonly object _locker = new object();
    readonly ConcurrentDictionary<int, string> _dict = new ConcurrentDictionary<int, string>();
    
    public bool TryRemove(int key, string value)
    {
        var success = false;
        lock (_locker)
        {
            if (_dict.ContainsKey(key) && _dict[key] == value)
            {
                string val;
                success = _dict.TryRemove(key, out val);
            }
        }
        return success;
    }
    

    话虽如此,似乎目标本质上是非原子的,这就是我们需要锁的原因。重要的是要问,你的目标是什么,你能以原子的方式表达目标。 ConcurrentDictionary 的 2 个有用方法包括 TryUpdate 和 AddOrUpdate。这些方法有什么帮助吗?

    【讨论】:

    • 您在有人已经发布了一个原子解决方案之后才回答,尽管我感谢您在回答中付出的努力。
    • 不客气!这里的每个答案都略有不同。这些差异在多线程代码中可能会产生很大的性能差异。在上面的代码中,字典中的值被检查而不删除。这很大,因为添加和删除是一项昂贵的操作。只有满足移除条件时,才会移除该值。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多