我有一个ConcurrentDictionaryWrapper
类,它封装了ConcurrentDictionary<K,V>
并实现了IProducerConsumerCollection
接口,所以我可以将它与BlockingCollection
一起使用。生产者将用一个键向BlockingCollection添加值。其思想是,如果键存在,我们将替换底层字典中的值。我的ConcurrentDictionaryWrapper.TryAdd()
方法如下:
public bool TryAdd(KeyValuePair<TKey, TValue> item)
{
_wrapped[item.Key] = item.Value
return true;
}
我看到的问题是,如果该值被替换,BlockingCollection将把它看作是一个附加值。
var wrapper = new ConcurrentDictionaryWrapper<string, object>();
var bc = new BlockingCollection<KeyValuePair<string, object>>(wrapper);
bc.TryAdd(new KeyValuePair<string, object>("key", new object()));
bc.TryAdd(new KeyValuePair<string, object>("key", new object()));
wrapper.Count; # returns 1
bc.Count; # returns 2
我不能在上面的TryAdd()
中返回false,因为BlockingCollection会引发InvalidOperationException。
有什么方法可以达到我想要的行为吗?我希望这尽可能简单,但似乎没有办法通过实现AddOrUpdate来实现BlockingCollection中的“IProducerConsumerCollection
”行为。在调用TryTake()
之前,我希望避免在BlockingCollection上调用TryAdd()
--我可能只是在字典周围使用一个标准锁,并将生产者/消费者与AutoResetEvent
同步。
发布于 2013-12-18 14:00:16
这里存在相当大的摩擦,BlockingCollection<>维护自己的计数,并且不使用IProducerConsumerCollection.Count实现。因此,由于您没有失败TryAdd(),所以BlockingCollection现在确信集合中有两个项。即使你只能找回其中的一个。因此,您当然不能保持原样,您的代码将始终处于死锁状态。
您需要的是一个BlockingCollection方法,如果项已经存在,则返回false或静默失败。但它没有一个,InvalidOperationException是不可避免的。你可以抓住它,但那太丑了。
另一种方法是would ()方法,如果返回true,可以使用该方法来避免调用TryAdd()。但是现在这个收藏不再是线程安全了。这就是为什么BlockingCollection没有这种方法的原因。
这是一个岩石和困难的地方,你不能让它按照设想的工作。你得自己转。最好偷取线程母版的代码,这篇杂志文章中的有界缓冲区应该是正确锁设计的良好开端。
发布于 2013-12-18 13:27:40
也许,将以前的项目标记为无效,而不是替换,可能更适合。保留一个包含每个值的布尔标志。添加带有现有键的项时,查找前一项,将其标志设置为false,并将标志设置为true的新项添加到true。
然后,当使用者获得该项目时,它将检查它是否有效。如果没有,它会扔掉它,并要求新的。
https://stackoverflow.com/questions/20658271
复制相似问题