У меня есть очередь, в которой я могу поставить в очередь разные потоки, поэтому я могу обеспечить две вещи:
- Запрос обрабатывается один за другим.
- Запрос обрабатывается в поступающем заказе
Второй момент важен. В противном случае достаточно простого критического раздела.
У меня разные группы запросов, и только внутри одной группы эти пункты должны быть выполнены. Запросы из разных групп могут выполняться одновременно.
Это выглядит так:
FTaskQueue.Enqueu('MyGroup');
try
Do Something (running in context of some thread)
finally
FTaskQueue.Dequeu('MyGroup');
end;
РЕДАКТИРОВАТЬ : Я удалил фактическую реализацию, потому что она скрывает проблему, которую я хочу решить
Мне это нужно, потому что у меня есть веб-сервер на основе Indy, который принимает запросы 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, может случиться так, что второй поток просто покинет критическую секцию и попытается заблокировать себя. Теперь планировщик потока возобновит работу первого потока, который попытается разблокировать следующий (второй) поток. Но второй поток еще не заблокирован, поэтому ничего не происходит. Теперь второй поток продолжается и блокируется, но это не правильно, потому что он не будет разблокирован. Если блокировка находится внутри критической секции, то критическая секция никогда не закрывается, и мы заходим в тупик.