如何在.NET ConcurrentDictionary中实现remove_if功能 [英] How to achieve remove_if functionality in .NET ConcurrentDictionary
问题描述
我有一种情况,我必须在 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屋!