windows - 如何等待多个事件?

标签 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;

我不知道在这种情况下调用inherited后发出事件信号是否可以,但这不是有问题的部分。

我知道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;

最佳答案

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

@Handles[1]

@Handles[0]

其他一些建议:

  1. 您必须立即调用 RaiseLastWin32Error(或者实际上是 RaiseLastOSError),以便 GetLastError 可以检索所需 API 调用的错误代码。您在设置标签标题后调用它,这很可能会导致意外调用 SetLastError
  2. 最好在重写的 DoTerminate 方法中设置事件。这样您就不会从析构函数调用 FEvent 上的方法。如果构造函数引发,则 FEvent 可以为 nil
  3. 您不应保留对自由终止线程的引用。由于线程对象可以随时释放,因此您很容易最终持有对已销毁对象的引用。

最后一点非常重要。当您循环调用 Terminate 的所有线程时,您显然没有遵守这一点。您需要决定是否使用 free-on-terminate,或者是否要保留对线程的引用。您必须选择一个或其他选项。你不能两全其美。

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

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

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

  • 停止使用 free-on-terminate 线程。
  • 删除所有事件。
  • 等待线程句柄。
  • 等待完成后释放线程。

仅当不需要等待线程时才使用 free-on-terminate 线程。

关于windows - 如何等待多个事件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26216568/

相关文章:

linux - 程序员应该使用什么工具从 Windows 机器在 Ubuntu 机器上工作

delphi - 要使使用Delphi创建的DLL在其他Delphi版本中良好运行,应遵循哪些原则?

java - 在 java 应用程序中使用 winapi

c++ - VS2010 中的 Windows 窗体应用程序无需 .NET?

平板电脑 : Will Eclipse run on windows tablet? 上的 Android 开发

C++ Windows Sleep() 不停止其他任务

windows - linux下maven-jetty-plugin无法绑定(bind)地址

python - 读取 Windows 文件而不阻止其他进程写入该文件

delphi - 利用多核cpu将程序转换为多线程

c# - 如何使用 C# 销毁第三方 WPF 子窗口?