2014-10-06 79 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