简体   繁体   中英

TryRemove a key-value pair from ConcurrentDictionary in C#

The scenario I have is I want a method on ConcurrentDictionary like this.

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
}

Is there a way to do this concurrently? I'm struggling to find an answer for this scenario, even though it seems like this is a common use case.

I could do something like this, but I want to avoid a lock if I'm already using a ConcurrentDictionary . I'd also have to have locks on GetOrAdd() or AddOrUpdate() calls elsewhere. It just seems like there should be a better way with a 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;
    }
}

Since ConcurrentDictionary<TKey, TValue> class implements (although explicitly) IDictionary<TKey, TValue> , thus ICollection<KeyValuePair<TKey, TValue>> , you can simply cast it to the later and use Remove method like this:

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

The implementation internally uses the same thread safe method (passing additionally the value to be checked) as the public TryRemove method - exactly as it should be.

Edit: Generally speaking, the method in question can be made available for any type implementing IDictionary<TKey, TValue> (or more precisely ICollection<KeyValuePair<TKey, TValue>> ) like Dictionary , ConcurrentDictionary etc. by introducing a custom extension method like this:

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

so the sample code becomes simply:

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

I'd do something like this

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

You could write an extension method like this one on 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;
    }
}

Below is a piece of code that simplify the steps a bit.

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

With that said, it seems the goal is non atomic in nature and this is why we have the need for a lock. It's important to ask, what is your goal and can you express the goal in an atomic way. 2 useful methods of ConcurrentDictionary include TryUpdate and AddOrUpdate. Would any of those methods help?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM