2014-10-06 60 views
2

在我正在使用的應用程序中有幾個線程。 這些線程被設置爲FreeOnTerminate,我不允許改變這種行爲。 我看到一些奇怪的老開發人員正在等待在主線程中的一些信號的方式,如下:如何等待多個事件?

令:

var FWaits: array of TEvent; 
var FThreads: array of TBkgThread; 
const C = 10; 

爲每個線程,我們有一個事件,然後Length(Threads) = Length(FWaits)

for I:= 0 to C-1 do 
    begin 
    FWaits[I]:= TSimpleEvent.Create; 
    FThreads[I]:= TBkgThread.Create(FWaits[I]); //The handle of the Event 
    end; 

    [CODE] 
    for I:= 0 to Length(FWaits)-1 do 
    case FWaits[I].WaitFor(INFINITE) of 
     wrError: begin 
       NotifyError; //Code Irrevelant; 
       Break; 
       end; 
     wrSignaled: TryNotifyUser(I); //Code Irrevelant; 
     wrAbandoned: TryNotifyAbandon(I); //Code Irrevelant; 
     wrTimeout: TryNotifyTimeOut(I); //Code Irrevelant; 
    end; 

這樣安排是在工作線程:

destructor TBkgThread.Destroy; 
begin 
    inherited; 
    FEvent.SetEvent; 
end; 

我不知道在這種情況下調用繼承的信號是否正常,但這不是問題的一部分。

我知道WaitForMultipleObjects的,所以我試圖減少打上[CODE]這個代碼:

var Handles: array of THandle; 

    SetLength(Handles, Length(FWaits)); 
    for I:= 0 to Length(FWaits)-1 do 
    Handles[I]:= FWaits[I].Handle; 

    case WaitForMultipleObjects(Length(Handles), @Handles[0], TRUE, INFINITE) of 
    WAIT_FAILED: begin Label1.Caption:= 'WAIT_FAILED'; RaiseLastWin32Error; end; 
    WAIT_OBJECT_0: Label1.Caption:= 'WAIT_OBJECT_0'; 
    WAIT_ABANDONED_0: Label1.Caption:= 'WAIT_ABANDONED_0'; 
    WAIT_TIMEOUT: Label1.Caption:= 'WAIT_TIMEOUT'; 
    end; 

但它提高了Windows錯誤:代碼6.

我怎樣才能正確地等待多個事件?

[更新]

TBkgThread = class(TThread) 
    private 
    FEvent: TEvent; 
    protected 
    procedure Execute; override; 
    public 
    constructor Create(AEvent: TEvent); 
    destructor Destroy; override; 
    end; 

    TForm1 = class(TForm) 
    edThreads: TLabeledEdit; 
    Button1: TButton; 
    Button2: TButton; 
    Label1: TLabel; 
    procedure Button1Click(Sender: TObject); 
    procedure Button2Click(Sender: TObject); 
    private 
    FThreads: array of TBkgThread; 
    FWaits: array of TEvent; 
    public 
    { Public declarations } 
    end; 

{ TBkgThread } 

constructor TBkgThread.Create(AEvent: TEvent); 
begin 
    inherited Create(False); 
    FreeOnTerminate:= True; 
    FEvent:= AEvent; 
end; 

destructor TBkgThread.Destroy; 
begin 
    inherited; 
    FEvent.SetEvent; 
end; 

procedure TBkgThread.Execute; 
var I,J,K: Integer; 
begin 
    while not Terminated do 
    begin 
    for I:= 0 to 10000 div 20 do 
     for J:= 0 to 10000 div 20 do 
     for K:= 0 to 10000 div 20 do; 
    Sleep(1000); 
    end; 
end; 

procedure TForm1.Button1Click(Sender: TObject); 
var 
    I, C: Integer; 
begin 
    C:= StrToIntDef(edThreads.Text, 0); 
    if C > 0 then 
    begin 
    SetLength(FThreads, C); 
    SetLength(FWaits, C); 
    for I:= 0 to C-1 do 
    begin 
     FWaits[I]:= TSimpleEvent.Create(); 
     FThreads[I]:= TBkgThread.Create(FWaits[I]); 
    end; 
    end; 
end; 

procedure TForm1.Button2Click(Sender: TObject); 
var I: Integer; 
    Handles: array of THandle; 
begin 
    for I:= 0 to Length(FWaits)-1 do 
    FThreads[I].Terminate; 

    SetLength(Handles, Length(FWaits)); 
    for I:= 0 to Length(FWaits)-1 do 
    Handles[I]:= FWaits[I].Handle; 

    case WaitForMultipleObjects(Length(Handles), @Handles[0], TRUE, INFINITE) of 
    WAIT_FAILED: begin RaiseLastWin32Error; Label1.Caption:= 'WAIT_FAILED'; end; 
    WAIT_OBJECT_0: Label1.Caption:= 'WAIT_OBJECT_0'; 
    WAIT_ABANDONED_0: Label1.Caption:= 'WAIT_ABANDONED_0'; 
    WAIT_TIMEOUT: Label1.Caption:= 'WAIT_TIMEOUT'; 
    end; 

// for I:= 0 to Length(FWaits)-1 do 
// case FWaits[I].WaitFor(INFINITE) of 
//  wrError: begin Label1.Caption:= 'WAIT_FAILED'; Break; end; 
//  wrSignaled: Label1.Caption:= 'WAIT_OBJECT_0'; 
//  wrAbandoned: Label1.Caption:= 'WAIT_ABANDONED_0'; 
//  wrTimeout: Label1.Caption:= 'WAIT_TIMEOUT'; 
// end; 
end; 
+0

我不知道SSCCE是否有必要。如果需要,我可以發佈。 – EProgrammerNotFound 2014-10-06 12:49:27

+0

你不能等待終止線程上的空閒。啓動後,您不能保留對它們的引用。您需要重新考慮設計無法更改的限制。這使得解決問題非常棘手。 – 2014-10-06 12:53:18

+0

我正在等待事件,而不是線程 – EProgrammerNotFound 2014-10-06 12:53:41

回答

3

您通過第二等待句柄,而不是第一個地址。更換

@Handles[1] 

@Handles[0] 

其他一些建議:

  1. 你必須調用RaiseLastWin32Error(或實際上RaiseLastOSError)立刻讓GetLastError可以爲所需的API調用檢索錯誤代碼。您在設置標籤標題後調用它,這很可能會導致偶然致電SetLastError
  2. 最好在重寫的DoTerminate方法中設置事件。這樣你就不會從析構函數調用FEvent的方法。如果構造函數提出,則FEvent可以是nil
  3. 你不應該保留對free-on-terminate線程的引用。由於線程對象可以在任何時候處理,因此您可以輕鬆完成對被銷燬對象的引用。

最後一點非常重要。當你繞過所有調用Terminate的線程時,你顯然不會堅持這一點。您需要決定是否使用free-on-terminate,或者是否要保留對線程的引用。你必須選擇一個或其他選項。你不能兩面都有。

如果您希望使用free-on-terminate線程,那麼您將需要使用單獨的事件來發信號通知取消。將該事件傳遞給施工線程。而不是在線程方法中測試Terminated,測試是否設置了該事件。設置事件當你想取消。

但是,這一切都很奇怪。你說你希望使用free-on-terminate線程,但是你也想等待所有的線程完成。使用free-on-terminate線程會迫使你創建額外的事件來處理等待和取消。因爲你不能保留對線程的引用。現在,你必須釋放你創建的事件。所以,你已經避免了釋放線程,但取而代之的是釋放事件的需求。

你的方法比它需要的複雜得多。在我看來,您應該:

  • 停止使用自由終止線程。
  • 刪除所有事件。
  • 等待線程句柄。
  • 當等待完成時釋放線程。

只有在不需要等待線程時才使用free-on-terminate線程。

+0

我認爲這是SSCCE中的一個錯字,但我仍然會檢查 – EProgrammerNotFound 2014-10-06 13:03:19

+0

不是,那是問題...我不相信 – EProgrammerNotFound 2014-10-06 13:03:50

+0

Arhg .... Facepalm – EProgrammerNotFound 2014-10-06 13:04:26