我的基本问题是,如果队列为空,则需要立即处理队列中的项目,或者将项目添加到队列中,如果有项目已经处理则离开。
我正在尝试一种使用peek来简化事物的技术,并且想知道可能存在的问题可能会阻碍。谢谢!
void SequenceAction(Action action) {
bool go = false;
lock (_RaiseEventQueueLock) {
_RaiseEventQueue.Enqueue(action);
go = (_RaiseEventQueue.Count == 1);
}
// 'go' can only be true if queue was empty when queue
// was locked and item was enqueued.
while (go) {
#if naive_threadsafe_presumption
// Peek is threadsafe because in effect this loop owns
// the zeroeth item in the queue for as long as the queue
// remains non-empty.
(_RaiseEventQueue.Peek())();
#else
Action a;
lock (_RaiseEventQueueLock) {
a = _RaiseEventQueue.Peek();
}
a();
#endif
// Lock the queue to see if any item was enqueued while
// the zeroeth item was being processed.
// Note that the code processing an item can call this
// function reentrantly, adding to its own todo list
// while insuring that that each item is processed
// to completion.
lock (_RaiseEventQueueLock) {
_RaiseEventQueue.Dequeue();
go = (_RaiseEventQueue.Count > 0);
}
}
}
答案 0 :(得分:1)
实际上,您的Peek
不是线程安全的。将项添加到队列可能会导致后端存储(最终是一个数组)的大小调整。我想这个队列是在一个循环缓冲区中实现的,带有用于插入和移除的头部和尾部索引。
想象一下,如果队列中有16个项目会发生什么。 Insert位于8,Remove位于9.队列已满。然后发生这种情况:
您可能能够通过以下方式解决该问题:
Action nextAction;
lock (_RaiseEventQueueLock)
{
nextAction = _RaiseEventQueue.Peek();
}
nextAction();
但是,我不会把我的职业生涯放在上面。我建议使用BlockingCollection和生产者/消费者设计。
可能的解决方法
我觉得以下情况应该符合你的意图。
private readonly object _queueLock = new object();
private readonly object _processLock = new object();
void SequenceAction(Action action)
{
lock (_queueLock)
{
_RaiseEventQueue.Enqueue(action);
}
if (Monitor.TryEnter(_processLock))
{
while (true)
{
Action a;
lock (_queueLock)
{
if (_RaiseEventQueue.Count == 0) return;
a = _RaiseEventQueue.Dequeue();
}
a();
}
Monitor.Exit(_processLock);
}
}
答案 1 :(得分:1)
// If action already in progress, add new
// action to queue and return.
// If no action in progress, begin processing
// the new action and continue processing
// actions added to the queue in the meantime.
void SequenceAction(Action action) {
lock (_SequenceActionQueueLock) {
_SequenceActionQueue.Enqueue(action);
if (_SequenceActionQueue.Count > 1) {
return;
}
}
// Would have returned if queue was not empty
// when queue was locked and item was enqueued.
for (;;) {
action();
lock (_SequenceActionQueueLock) {
_SequenceActionQueue.Dequeue();
if (_SequenceActionQueue.Count == 0) {
return;
}
action = _SequenceActionQueue.Peek();
}
}
}