TThreadedQueue(Generics.Collections)を単一プロデューサの複数コンシューマスキームで使用しようとしています。 (Delphi-XE)である。 アイデアは、オブジェクトをキューにプッシュし、複数のワーカースレッドにキューを排水させることです。TThreadedQueueは複数のコンシューマーに対応していませんか?
ただし、期待どおりに動作しません。 2つ以上のワーカースレッドがPopItemを呼び出すと、TThreadedQueueからアクセス違反がスローされます。
PopItemの呼び出しがクリティカルセクションでシリアル化されている場合、すべて正常です。
確かにTThreadedQueueは複数のコンシューマを処理できなければならないので、何か不足していますか、これはTThreadedQueueの純粋なバグですか?
ここでは、エラーを生成する簡単な例を示します。
program TestThreadedQueue;
{$APPTYPE CONSOLE}
uses
// FastMM4 in '..\..\..\FastMM4\FastMM4.pas',
Windows,
Messages,
Classes,
SysUtils,
SyncObjs,
Generics.Collections;
type TThreadTaskMsg =
class(TObject)
private
threadID : integer;
threadMsg : string;
public
Constructor Create(ID : integer; const msg : string);
end;
type TThreadReader =
class(TThread)
private
fPopQueue : TThreadedQueue<TObject>;
fSync : TCriticalSection;
fMsg : TThreadTaskMsg;
fException : Exception;
procedure DoSync;
procedure DoHandleException;
public
Constructor Create(popQueue : TThreadedQueue<TObject>;
sync : TCriticalSection);
procedure Execute; override;
end;
Constructor TThreadReader.Create(popQueue : TThreadedQueue<TObject>;
sync : TCriticalSection);
begin
fPopQueue:= popQueue;
fMsg:= nil;
fSync:= sync;
Self.FreeOnTerminate:= FALSE;
fException:= nil;
Inherited Create(FALSE);
end;
procedure TThreadReader.DoSync ;
begin
WriteLn(fMsg.threadMsg + ' ' + IntToStr(fMsg.threadId));
end;
procedure TThreadReader.DoHandleException;
begin
WriteLn('Exception ->' + fException.Message);
end;
procedure TThreadReader.Execute;
var signal : TWaitResult;
begin
NameThreadForDebugging('QueuePop worker');
while not Terminated do
begin
try
{- Calling PopItem can return empty without waittime !? Let other threads in by sleeping. }
Sleep(20);
{- Serializing calls to PopItem works }
if Assigned(fSync) then fSync.Enter;
try
signal:= fPopQueue.PopItem(TObject(fMsg));
finally
if Assigned(fSync) then fSync.Release;
end;
if (signal = wrSignaled) then
begin
try
if Assigned(fMsg) then
begin
fMsg.threadMsg:= '<Thread id :' +IntToStr(Self.threadId) + '>';
fMsg.Free; // We are just dumping the message in this test
//Synchronize(Self.DoSync);
//PostMessage(fParentForm.Handle,WM_TestQueue_Message,Cardinal(fMsg),0);
end;
except
on E:Exception do begin
end;
end;
end;
except
FException:= Exception(ExceptObject);
try
if not (FException is EAbort) then
begin
{Synchronize(} DoHandleException; //);
end;
finally
FException:= nil;
end;
end;
end;
end;
Constructor TThreadTaskMsg.Create(ID : Integer; Const msg : string);
begin
Inherited Create;
threadID:= ID;
threadMsg:= msg;
end;
var
fSync : TCriticalSection;
fThreadQueue : TThreadedQueue<TObject>;
fReaderArr : array[1..4] of TThreadReader;
i : integer;
begin
try
IsMultiThread:= TRUE;
fSync:= TCriticalSection.Create;
fThreadQueue:= TThreadedQueue<TObject>.Create(1024,1,100);
try
{- Calling without fSync throws exceptions when two or more threads calls PopItem
at the same time }
WriteLn('Creating worker threads ...');
for i:= 1 to 4 do fReaderArr[i]:= TThreadReader.Create(fThreadQueue,Nil);
{- Calling with fSync works ! }
//for i:= 1 to 4 do fReaderArr[i]:= TThreadReader.Create(fThreadQueue,fSync);
WriteLn('Init done. Pushing items ...');
for i:= 1 to 100 do fThreadQueue.PushItem(TThreadTaskMsg.Create(i,''));
ReadLn;
finally
for i:= 1 to 4 do fReaderArr[i].Free;
fThreadQueue.Free;
fSync.Free;
end;
except
on E: Exception do
begin
Writeln(E.ClassName, ': ', E.Message);
ReadLn;
end;
end;
end.
更新:TThreadedQueueは、Delphi XE2に固定されてクラッシュさせTMonitorでエラーが発生しました。
更新2:上記のテストでは、キューが空の状態であることを示しています。 Darian Miller氏は、キューをフル状態にしてもXE2のエラーを再現できることを発見しました。エラーはもう一度TMonitorにあります。詳細は下記の彼の答えを見てください。また、QC101114へのリンクもあります。
アップデート3: デルファイXE2更新4でTThreadedQueue
の問題を治すでしょうTMonitor
のために発表の修正がありました。これまでのテストでは、TThreadedQueue
のエラーは再現できません。 キューが空でいっぱいの場合、単一のプロデューサ/複数のコンシューマスレッドをテストしました。 複数のプロデューサ/複数のコンシューマもテスト済みです。私は読者スレッドとライタースレッドを1から100に変えました。しかし、歴史を知って、私はTMonitor
を壊すために他人を挑戦する。
こんにちは! StackOverflowへようこそ。これは良い質問ですが、コードが少し違って投稿されたかどうかをテストする方が簡単かもしれません。対応するDFMなしで.pasの半分のフォームを追加しました。これは、私たちが複製して調査することを困難にしています。問題はUI関連のようではないので、これをコンソールアプリケーションに減らす方法はありますか?ありがとう。 –
メイソン、コンソールアプリが終了しました。 –
XE2にまだ問題があります。 –