我有一个队列,可以将不同的线程入队,因此我可以保证两件事情:
- 请求按顺序一个一个处理。
- 请求按照到达的顺序进行处理。
第二点非常重要。否则,仅使用简单的关键段就足够了。 我有不同组的请求,只有在单个组内才需要满足这些条件。来自不同组的请求可以并发运行。
它看起来像这样:
FTaskQueue.Enqueu('MyGroup');
try
Do Something (running in context of some thread)
finally
FTaskQueue.Dequeu('MyGroup');
end;
编辑: 我已经删除了实际的实现,因为它隐藏了我想要解决的问题。
我需要这个功能是因为我有一个基于Indy的Web服务器,可以接受HTTP请求。首先,我找到请求对应的会话。然后执行该会话的请求(代码)。我可能会收到同一会话的多个请求(即在第一个请求仍在处理时可能会收到新请求),它们必须按照正确的到达顺序逐一执行。因此,我需要一个通用的同步队列,以便在这种情况下可以将请求排队。我无法控制线程,每个请求可能在不同的线程中执行。
这种情况下最佳(常见)的方法是什么?问题在于Enqueue和Dequeue必须是原子操作,以保留正确的顺序。我的当前实现存在实质性瓶颈,但它可以工作。
编辑: 下面是原子Enqueue / Dequeue操作的问题
你通常会这样做:
procedure Enqueue;
begin
EnterCriticalSection(FCritSec);
try
DoEnqueue;
finally
LeaveCriticalSection(FCritSec);
end;
BlockTheCurrentThread; // here the thread blocks itself
end;
procedure Dequeue;
begin
EnterCriticalSection(FCritSec);
try
DoDequeue;
UnblockTheNextThread; // here the thread unblocks another thread
finally
LeaveCriticalSection(FCritSec);
end;
end;
现在的问题是这不是原子操作。如果已经有一个线程在队列中,另一个线程调用Enqueue,第二个线程会离开临界区并尝试阻塞自己。现在线程调度程序将恢复第一个线程,它将尝试解除下一个(第二个)线程的阻塞。但第二个线程尚未被阻塞,因此什么也不会发生。现在第二个线程继续并阻塞自己,但这是不正确的,因为它将无法解除阻塞。如果阻塞在临界区内,则临界区永远不会离开,我们就会出现死锁。