我有一个队列,我可以在其中将不同的线程排入队列,所以我可以保证两件事:
- 请求被一一处理。
- 请求按到达顺序处理
第二点很重要。否则一个简单的临界区就足够了。我有不同的请求组,只有在一个组内必须满足这些点。来自不同组的请求可以并发运行。
它看起来像这样:
FTaskQueue.Enqueu('MyGroup');
try
Do Something (running in context of some thread)
finally
FTaskQueue.Dequeu('MyGroup');
end;
编辑:我已经删除了实际的实现,因为它隐藏了我想要解决的问题
我需要这个,因为我有一个基于 Indy 的 Web 服务器,它接受 http 请求。首先,我为请求找到一个对应的会话。然后为该会话执行请求(代码)。我可以为同一个会话获得多个请求(读取我可以在第一个仍在处理时获得新请求)并且它们必须以正确的到达顺序一个接一个地执行。所以我寻求一个可以在这种情况下使用的通用同步队列,以便可以对请求进行排队。我无法控制线程,每个请求都可以在不同的线程中执行。
解决此类问题的最佳(通常)方法是什么?问题是 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,则可能会发生第二个线程将离开临界区并尝试阻塞自己。现在线程调度程序将恢复第一个线程,它将尝试解除对下一个(第二个)线程的阻塞。但是第二个线程还没有被阻塞,所以什么也没有发生。现在第二个线程继续并阻塞自己,但这是不正确的,因为它不会被解除阻塞。如果阻塞在临界区内,则临界区永远不会离开,我们就会陷入死锁。