我遇到了一個尷尬的問題。在我的應用程式中,我經常這樣做
TThread.createAnonymousThread(
procedure
....
end).start
我遇到的問題是,當我關閉應用程式的主表單時,有時在 Tform.destroy finished 之后,其中一些 AnonymousThread 仍然存在。他們是否在我的 Tform.destroy 中等待所有那些 AnonymousThread (在整個應用程式中的任何地方都創建了一點)在繼續之前成功終止?
我發現這種方式可以列出所有正在運行的執行緒(來自
uj5u.com熱心網友回復:
您面臨的核心問題不是來自匿名執行緒本身,而是來自自毀的匿名執行緒 - 那些已經FreeOnTerminate設定的匿名執行緒。
為了等待執行緒,您需要參考執行緒或其句柄(Windows 平臺)。因為您正在處理自毀執行緒,所以參考執行緒不是一種選擇,因為一旦您啟動執行緒,您就不再被允許觸摸該參考。
Delphi RTL 在應用程式關閉期間不會對自毀匿名執行緒執行任何清理,因此在您的應用程式主表單被破壞后,這些執行緒將被作業系統殺死,因此您的問題。
允許您等待匿名執行緒的解決方案之一,不需要任何復雜的內務管理和維護任何型別的串列,并且還需要對代碼進行最少的更改,可以通過簡單的查找和替換來完成,TCountdownEvent使用計算執行緒。
這需要替換為TThread.CreateAnonymousThread與匿名執行緒具有相同行為的自定義執行緒類TAnoThread.Create(如果您愿意,您可以添加靜態工廠方法,而不是直接呼叫建構式),除了它的實體將被計算在內并且您將能夠等待所有此類執行緒完成運行。
type
TAnoThread = class(TThread)
protected
class var
fCountdown: TCountdownEvent;
class constructor ClassCreate;
class destructor ClassDestroy;
public
class procedure Shutdown; static;
class function WaitForAll(Timeout: Cardinal = INFINITE): TWaitResult; static;
protected
fProc: TProc;
procedure Execute; override;
public
constructor Create(const aProc: TProc);
end;
class constructor TAnoThread.ClassCreate;
begin
fCountdown := TCountdownEvent.Create(1);
end;
class destructor TAnoThread.ClassDestroy;
begin
fCountdown.Free;
end;
class procedure TAnoThread.Shutdown;
begin
fCountdown.Signal;
end;
class function TAnoThread.WaitForAll(Timeout: Cardinal): TWaitResult;
begin
Result := fCountdown.WaitFor(Timeout);
end;
constructor TAnoThread.Create(const aProc: TProc);
begin
inherited Create(True);
fProc := aProc;
FreeOnTerminate := True;
end;
procedure TAnoThread.Execute;
begin
if fCountdown.TryAddCount then
try
fProc;
finally
fCountdown.Signal;
end;
end;
然后您可以在表單解構式或任何其他適當的位置添加以下代碼,并等待所有匿名執行緒完成運行。
destructor TForm1.Destroy;
begin
TAnoThread.Shutdown;
while TAnoThread.WaitForAll(100) <> wrSignaled do
CheckSynchronize;
inherited;
end;
原理如下:倒計時事件以值 1 創建,當該值達到 0 時,將發出事件信號。要啟動關閉,您呼叫Shutdown將減少初始計數的方法。您不能多次呼叫此方法,因為它會弄亂計數。
當匿名執行緒Execute方法啟動時,它會首先嘗試增加計數。如果它不能這樣做,則意味著倒計時事件已經發出信號,執行緒將在不呼叫其匿名方法的情況下終止,否則匿名方法將運行,完成后計數將減少。
如果匿名執行緒使用TThread.Synchronize呼叫,你不能只呼叫WaitForAll,因為從主執行緒呼叫它會死鎖。為了防止在等待執行緒完成時出現死鎖,您需要呼叫CheckSynchronize來處理掛起的同步請求。
此解決方案計算TAnoThread類的所有執行緒,無論它們是否自毀。這可以很容易地更改為僅計算已FreeOnTerminate設定的那些。
此外,當您呼叫Shutdown并且仍然有一些正在運行的執行緒時,新執行緒仍然可以在該點啟動,因為甚至沒有發出倒計時信號。如果你想從那時起阻止新執行緒,你需要添加一個布爾標志,表明你已經啟動了關閉程序。
uj5u.com熱心網友回復:
從行程中讀取所有執行緒,然后嘗試找出要等待的執行緒,這聽起來很痛苦。
如果您真的不想存盤匿名執行緒的參考(順便說一下,它不應該是 FreeOnTerminate,因為如果執行緒在您等待它之前結束,這會導致懸空參考)然后構建一個包裝器TThread.CreateAnonymousThread或者TTask.Run哪個內部存盤并封裝WaitFor. 您甚至可以花哨并添加一個額外的組鍵,這樣您就可以創建和等待不同的執行緒/任務集而不是全部。
uj5u.com熱心網友回復:
TThread將 .回傳的參考推入執行緒安全串列CreateAnonymousThread,確保在執行緒終止時保持同步并實作您自己的WaitForAll.
或者,考慮使用TTask類進行簡單的并行執行緒管理。它已經實作了WaitForAll方法。
示例代碼取自 Delphi幫助:
procedure TForm1.MyButtonClick(Sender: TObject);
var
tasks: array of ITask;
value: Integer;
begin
Setlength (tasks ,2);
value := 0;
tasks[0] := TTask.Create (procedure ()
begin
sleep(3000);
TInterlocked.Add (value, 3000);
end);
tasks[0].Start;
tasks[1] := TTask.Create (procedure ()
begin
sleep (5000);
TInterlocked.Add (value, 5000);
end);
tasks[1].Start;
TTask.WaitForAll(tasks);
ShowMessage ('All done: ' value.ToString);
end;
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/474102.html
標籤:德尔福
上一篇:自洗掉按鈕
下一篇:程式物件如何改變_self?
