如何在.NET ConcurrentDictionary中实现remove_if功能 [英] How to achieve remove_if functionality in .NET ConcurrentDictionary

查看:60
本文介绍了如何在.NET ConcurrentDictionary中实现remove_if功能的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一种情况,我必须在 ConcurrentDictionary 中将给定密钥的引用计数对象保留在其中,如果引用计数达到 0 ,我想删除密钥.这必须是线程安全的,因此我计划使用 ConcurrentDictionary .

I have a scenario where I have to keep reference counted object for given key in the ConcurrentDictionary, if reference count reaches 0, I want to delete the key. This has to be thread safe hence I am planning to use the ConcurrentDictionary.

示例程序如下.在并发字典中,我有key和value,值是KeyValuePair,其中包含我的自定义对象和引用计数.

Sample program as follows. In the concurrent dictionary, I have key and value , the value is KeyValuePair which holds my custom object and reference count.

ConcurrentDictionary<string, KeyValuePair<object, int>> ccd = 
    new ConcurrentDictionary<string, KeyValuePair<object, int>>();

// following code adds the key, if not exists with reference 
// count   for  my custom object to 1
// if the key already exists it increments the reference count

var addOrUpdateValue = ccd.AddOrUpdate("mykey",
    new KeyValuePair<object, int>(new object(), 1),
    (k, pair) => new KeyValuePair<object, int>(pair.Key, pair.Value + 1));

现在,我想要一种当引用计数达到0时删除键的方法.我在想,使用 ConcurrentDictionary 上的方法,该方法需要键和谓词,如果谓词返回'true,则删除键.'.例子.

Now I want a way to remove the key when the reference count reaches to 0. I was thinking , remove method on ConcurrentDictionary which takes key and predicate , removes the key if the predicate return 'true'. Example.

ConcurrentDictionary.remove(TKey, Predicate<TValue> ). 

ConcurrentDictionary 上没有这样的方法,问题是如何以线程安全的方式进行相同的操作?.

There is no such method on ConcurrentDictionary, question is how to do the same in thread safe way ?.

推荐答案

.NET不会直接公开 RemoveIf ,但是确实公开了使它工作而不需要自己做的必要构建基块锁定.

.NET doesn't expose a RemoveIf directly, but it does expose the building blocks necessary to make it work without doing your own locking.

ConcurrentDictionary 实现 ICollection< T> ,该软件具有 Remove ,该代码接受并测试完整的 KeyValuePair 而不只是一个钥匙.尽管被隐藏,该 Remove 仍然是线程安全的,我们将使用它来实现此目的.要做到这一点的一个警告是, Remove 使用 EqualityComparer< T> .Default 来测试该值,因此它必须具有相等的可比性.您当前的版本不是,因此我们将重新实现它:

ConcurrentDictionary implements ICollection<T>, which has a Remove that takes and tests for a full KeyValuePair instead of just a key. Despite being hidden, this Remove is still thread-safe and we'll use it to implement this. One caveat for this to work is that Remove uses EqualityComparer<T>.Default to test the value, so it must be equality comparable. Your current one is not, so we'll re-implement that as such:

struct ObjectCount : IEquatable<ObjectCount>
{
    public object Object { get; }
    public int Count { get; }

    public ObjectCount(object o, int c)
    {
        Object = o;
        Count = c;
    }

    public bool Equals(ObjectCount o) =>
       object.Equals(Object, o.Object) && Count == o.Count;

    public override bool Equals(object o) =>
       (o as ObjectCount?)?.Equals(this) == true;

    // this hash combining will work but you can do better.
    // it is not actually used by any of this code.
    public override int GetHashCode() =>
       (Object?.GetHashCode() ?? 0) ^ Count.GetHashCode();
}

最后,我们将定义一种方法来增加/减少字典中的计数:

And finally, we'll define a method to increment/decrement counts from your dictionary:

void UpdateCounts(ConcurrentDictionary<string, ObjectCount> dict, string key, int toAdd)
{
    var addOrUpdateValue = dict.AddOrUpdate(key,
        new ObjectCount(new object(), 1),
        (k, pair) => new ObjectCount(pair.Key, pair.Value + toAdd));

    if(addOrUpdateValue.Count == 0)
    {
        ((ICollection<KeyValuePair<string, ObjectCount>>)dict).Remove(
            new KeyValuePair<string, ObjectCount>(key, addOrUpdateValue));
    }
}

AddOrUpdate Remove 的调用之间可能会更改该键的值,但这对我们来说无关紧要:因为 Remove 测试完整的 KeyValuePair ,只有在更新后该值未更改的情况下,它才会将其删除.

The value for that key might be changed between the calls of AddOrUpdate and Remove, but that doesn't matter to us: because Remove tests the full KeyValuePair, it will only remove it if the value hasn't changed since the update.

这是常见的无锁模式,它是设置更改,然后使用最终线程安全的操作来安全地提交"更改(仅在此期间未更新我们的数据结构的情况下).

This is the common lock-free pattern of setting up a change and then using a final thread-safe op to safely "commit" the change only if our data structure hasn't been updated in the meantime.

这篇关于如何在.NET ConcurrentDictionary中实现remove_if功能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆