如果满足某些条件,我怎样才能使ConcurrentQueue
的下一个元素出列?
E.g。如果要出列的下一个项目满足某个条件,则将其出列,否则将其保留。基本上是'DequeueIf'
或'TryDequeueIf'
方法
示例:
var myQueue = new ConcurrentQueue<int>()
...
int item;
// only dequeue if the next item is 0
bool success = myQueue.TryDequeueIf(out item, x=>x==0)
当然可以先调用TryPeek
,检查条件,然后TryDequeue
,但这不再是线程安全的。
我可以将整个TryPeek & TryDequeue
包装成一个锁,但这有点违背了使用ConcurrentQueue的目的;并且意味着所有正常的无条件出列也必须被锁定。我不确定我是否甚至必须锁定每个Enqueue
才能保存。如果可能的话,我想避免实施我自己的锁定策略可能的陷阱。
是否有使用.net4.0 ConcurrentQueue
类或其他并发类之一的无锁解决方案?
答案 0 :(得分:3)
使用内置方法无法做到这一点。怎么办?
答案 1 :(得分:2)
作为@usr answer的附加组件:
我可以整理
的目的TryPeek
&amp;锁定TryDequeue
,但这有点违背了使用ConcurrentQueue
不是真的,或者至少不完全,因为其主要目的之一是协调生产者/消费者集合点,在这种情况下使用锁定不会阻止任何生产者,只阻止其他消费者。但这确实意味着您将需要对所有其他变异读取操作使用相同的锁。
因此,如果这是不可接受的,您将不得不
where
过滤器观察的Rx和消费者。答案 2 :(得分:1)
大多数concurrentQueue实现使用无锁实现允许许多生产者和使用者同时编写队列而不会相互阻塞(除了偶尔的旋转重试)
条件出队的基本要求是'pop if top on'函数的实现:'bool dequeueIf(T)//如果T仍然是头,则退出T,如果不是头,则返回false;这允许你:
T t=null;
do{ // need to spin in case the head changes between the peek and dequeue attempt
done=-1; // assume failure
T t2=peek(),
if (t!=null && someComplexCondition(t)) {// then it's what we want
if(!dequeueIf(T)) // check to see if the head is changed
{done=0;} // spin if the head is changed
else {done=1;t=t2;} // it meets the criteria, and we de-queued it.
}
} while (done==0);
// done = -1 == head does not meet criteria
// done = 1 == T contains poped item that met criteria
dequeueIf(T)需要在队列中实现。
如果是.Net ConcurrentQueue,则需要修改版本Dequeue(输出T结果):Dequeue(ref T toRemove)。
这将传递给concurrentQueue.Segment.TryRemove的新版本(输出T结果):concurrentQueue.Segment.TryRemove(ref T toRemove)
此函数需要在执行compareExchange去队列之前,首先将'local'处的T与toRemove进行比较,并且只有当它仍然位于顶部时才将其删除(否则它可以假设另一个线程TryRemove已经弹出关闭队列的值,所以你不能返回有问题的值; toRemove应该是否为空。)
请注意;你不能重载f(out T)和f(ref T),所以返回bool表示你现在'拥有'你请求弹出的T似乎是一个合理的解决方案,否则我会使用f的重载(T toRemove )允许原始函数签名不变。