有一種情況,用戶可能會觸發一些耗時的作業,這些作業需要按照用戶的操作順序執行。如何在Delphi Xe2中創建一個線程隊列?
我一直在尋找類TThreadedQueue
來存儲線程。這是一個開始的好地方嗎?還是有更合適的方法來做到這一點?
有一種情況,用戶可能會觸發一些耗時的作業,這些作業需要按照用戶的操作順序執行。如何在Delphi Xe2中創建一個線程隊列?
我一直在尋找類TThreadedQueue
來存儲線程。這是一個開始的好地方嗎?還是有更合適的方法來做到這一點?
下面你可以找到一個WorkerThreadPool的例子:
{~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
Twin_WorkerThreadPool = class;
{~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
Twin_WorkerThread = class(TThread)
private
FProc: TProc;
FProcReadySignal: Tevent;
FProcFinishedSignal: Tevent;
protected
procedure Execute; override;
public
constructor Create;
destructor Destroy; override;
procedure ExecuteAndWaitProc(const AProc: TProc);
property ProcFinishedSignal: Tevent read FProcFinishedSignal;
end;
{~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
Twin_WorkerThreadPool = class(TObject)
private
fPool: TObjectList<Twin_WorkerThread>;
fSignal: Tevent;
public
constructor Create(const aThreadCount: integer);
destructor Destroy; override;
procedure ExecuteAndWaitProc(const AProc: TProc);
procedure Enqueue(const Value: Twin_WorkerThread);
function Dequeue: Twin_WorkerThread;
end;
{***********************************}
constructor Twin_WorkerThread.Create;
begin
FProc := nil;
FProcReadySignal := TEvent.Create(nil, false{ManualReset}, false, '');
FProcFinishedSignal := TEvent.Create(nil, false{ManualReset}, false, '');
inherited Create(False); // see http://www.gerixsoft.com/blog/delphi/fixing-symbol-resume-deprecated-warning-delphi-2010
end;
{***********************************}
destructor Twin_WorkerThread.Destroy;
begin
Terminate;
FProcReadySignal.setevent;
WaitFor;
ALFreeAndNil(FProcReadySignal);
ALFreeAndNil(FProcFinishedSignal);
inherited;
end;
{**********************************}
procedure Twin_WorkerThread.Execute;
begin
while True do begin
try
//wait the signal
FProcReadySignal.WaitFor(INFINITE);
//if terminated then exit
if Terminated then Break;
//execute fProc
if assigned(FProc) then FProc();
//signal the proc is finished
FProcFinishedSignal.SetEvent;
except
//hide the exception
end;
end;
end;
{*****************************************************************}
procedure Twin_WorkerThread.ExecuteAndWaitProc(const AProc: TProc);
begin
fProc := AProc;
FProcFinishedSignal.ResetEvent;
FProcReadySignal.SetEvent;
FProcFinishedSignal.WaitFor(INFINITE);
fProc := nil;
end;
{********************************************************************}
constructor Twin_WorkerThreadPool.Create(const aThreadCount: integer);
var i: integer;
begin
fPool := TObjectList<Twin_WorkerThread>.create(false{aOwnObjects});
fSignal := TEvent.Create(nil, true{ManualReset}, false, '');
for I := 0 to aThreadCount - 1 do
fPool.Add(Twin_WorkerThread.Create)
end;
{***************************************}
destructor Twin_WorkerThreadPool.Destroy;
var aWorkerThread: Twin_WorkerThread;
i: integer;
begin
for I := 0 to fPool.Count - 1 do begin
aWorkerThread := fPool[i];
fPool[i] := nil;
ALFreeAndNil(aWorkerThread);
end;
ALFreeAndNil(fPool);
ALFreeAndNil(fSignal);
inherited Destroy;
end;
{*********************************************************************}
procedure Twin_WorkerThreadPool.ExecuteAndWaitProc(const AProc: TProc);
var aThread: Twin_WorkerThread;
begin
aThread := Dequeue;
try
aThread.ExecuteAndWaitProc(aProc);
finally
Enqueue(aThread);
end;
end;
{**********************************************************************}
procedure Twin_WorkerThreadPool.Enqueue(const Value: Twin_WorkerThread);
begin
Tmonitor.Enter(fPool);
try
fPool.Add(Value);
if fPool.Count = 1 then fSignal.SetEvent;
finally
Tmonitor.Exit(fPool);
end;
end;
{********************************************************}
function Twin_WorkerThreadPool.Dequeue: Twin_WorkerThread;
begin
Tmonitor.Enter(self); // << only one thread can process the code below
try
Tmonitor.Enter(fPool);
try
if Fpool.Count > 0 then begin
result := fPool[Fpool.Count - 1];
fPool.Delete(Fpool.Count - 1);
exit;
end;
fSignal.ResetEvent;
finally
Tmonitor.Exit(fPool);
end;
while True do begin // << their is a bug on ios with tevent - http://stackoverflow.com/questions/39884521/why-i-get-an-exception-argument-out-of-range
fSignal.WaitFor(Infinite);
Tmonitor.Enter(fPool);
try
if fPool.Count > 0 then begin
result := fPool[Fpool.Count - 1];
fPool.Delete(Fpool.Count - 1);
exit;
end
else begin
{$IFDEF DEBUG}
ALLog('Twin_WorkerThreadPool.Dequeue', 'fSignal.ResetEvent didn''t work as expected!', TalLogType.Error);
{$ENDIF}
end;
finally
Tmonitor.Exit(fPool);
end;
end;
finally
Tmonitor.exit(self);
end;
end;
感謝您的代碼。我去編譯它,發現一個編譯器錯誤。 ALFreeAndNil從哪裏來?它有什麼作用? –
aah對不起alfreeandnil來自alcinoe(你可以在網上找到代碼)...... alfreeandnil完全像freeandnil一樣,除了它使用dispose而不是免費來避免內存泄漏 – loki
如果只有一個生產者輸入作業,並且作業必須按順序調用一個新的作業無法啓動,直到前準備好,隊列不需要是線程安全的。作業完成後,只需在主線程中觸發一個事件即可啓動隊列中的下一個作業。 –
爲什麼要存儲線程。存儲任務。有一個線程可以逐一完成新的任務。 –