如何等待多个事件?

时间:2014-10-06 12:48:22

标签: windows delphi winapi delphi-7

我正在使用的应用程序中有很少的线程。 这些线程设置为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.

如何才能正确等待多个事件?

[UPDATE]

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;

1 个答案:

答案 0 :(得分:3)

您传递第二个等待句柄的地址而不是第一个等待句柄的地址。取代

@Handles[1]

@Handles[0]

其他一些建议:

  1. 您必须立即致电RaiseLastWin32Error(或确实RaiseLastOSError),以便GetLastError可以检索所需API调用的错误代码。您在设置标签标题后调用它,这很可能会导致偶然调用SetLastError
  2. 最好以重写的DoTerminate方法设置事件。这样你就不会从析构函数中调用FEvent上的方法。如果构造函数引发,则FEvent可以是nil
  3. 您不应保留对free-on-terminate线程的引用。由于线程对象可以随时处理,因此您最终可以容易地保存对被破坏对象的引用。
  4. 最后一点非常重要。当你绕过调用Terminate的所有线程时,你显然不遵守这一点。您需要决定是否使用free-on-terminate,或者是否要保留对线程的引用。你必须选择一个或其他选项。你无法双管齐下。

    如果您希望使用免费终止线程,那么您需要使用单独的事件来发出取消信号。将该事件传递给构造中的线程。而不是在线程方法中测试Terminated,而不是测试正在设置的事件。您希望取消时设置活动。

    然而,所有这些都很奇怪。你说你希望使用free-on-terminate线程,但是你也想等待所有的线程完成。然后使用免费终止线程会强制您创建额外的事件来处理等待和取消。因为您无法保留对线程的引用。现在,您必须释放您创建的事件。因此,您已经避免了必须释放线程,但将其替换为释放事件的要求。

    你的方法比它需要的要复杂得多。在我看来,你应该:

    • 停止使用免费终止线程。
    • 删除所有活动。
    • 等待线程句柄。
    • 等待完成后释放线程。

    当您不需要等待线程时,只使用free-on-terminate线程。