さまざまなスレッドをキューに入れることができるキューがあるので、次の2つのことを保証できます。
- リクエストは1つずつ処理されます。
- リクエストは到着順に処理されます
2点目は重要です。それ以外の場合は、単純なクリティカルセクションで十分です。私にはさまざまなリクエストのグループがあり、単一のグループ内でのみこれらのポイントを満たさなければなりません。異なるグループからのリクエストは同時に実行できます。
次のようになります。
FTaskQueue.Enqueu('MyGroup');
try
Do Something (running in context of some thread)
finally
FTaskQueue.Dequeu('MyGroup');
end;
編集:解決したい問題が隠されているため、実際の実装を削除しました
これが必要なのは、httpリクエストを受け入れるIndyベースのWebサーバーがあるからです。まず、リクエストに対応するセッションを見つけます。次に、そのセッションに対して要求(コード)が実行されます。同じセッションに対して複数のリクエストを取得でき(最初のリクエストがまだ処理されている間に新しいリクエストを取得できることを読んでください)、それらは到着の正しい順序で1つずつ実行する必要があります。そのため、このような状況で使用できる汎用同期キューを探して、リクエストをキューに入れることができるようにします。私はスレッドを制御できず、各リクエストは異なるスレッドで実行される可能性があります。
この種の問題に対する最良の(通常の)アプローチは何ですか?問題は、正しい順序が維持されるように、エンキューとデキューはアトミック操作でなければならないことです。私の現在の実装にはかなりのボトルネックがありますが、機能します。
編集:以下はアトミックエンキュー/デキュー操作の問題です
あなたは通常このようなことをします:
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;
ここでの問題は、これがアトミックではないということです。1つのスレッドがすでにキューにあり、別のスレッドが来てEnqueueを呼び出す場合、2番目のスレッドがクリティカルセクションを離れて自分自身をブロックしようとする可能性があります。これで、スレッドスケジューラは最初のスレッドを再開し、次の(2番目の)スレッドのブロックを解除しようとします。ただし、2番目のスレッドはまだブロックされていないため、何も起こりません。これで、2番目のスレッドが続行され、それ自体がブロックされますが、ブロックが解除されないため、これは正しくありません。ブロッキングがクリティカルセクション内にある場合、そのクリティカルセクションが離れることはなく、デッドロックが発生します。