Windows 如何等待多个事件?
我正在使用的应用程序中几乎没有线程。 这些线程设置为FreeOnTerminate,不允许我更改此行为。 我看到老开发人员在主线程中等待信号的方式有些奇怪,如下所示: 让我们: 对于每个线程,我们都有一个事件,然后Windows 如何等待多个事件?,windows,delphi,winapi,delphi-7,Windows,Delphi,Winapi,Delphi 7,我正在使用的应用程序中几乎没有线程。 这些线程设置为FreeOnTerminate,不允许我更改此行为。 我看到老开发人员在主线程中等待信号的方式有些奇怪,如下所示: 让我们: 对于每个线程,我们都有一个事件,然后Length(Threads)=Length(FWaits) 这是在工作线程中编程的: destructor TBkgThread.Destroy; begin inherited; FEvent.SetEvent; end; 在这种情况下,我不知道调用inherited后是
Length(Threads)=Length(FWaits)
这是在工作线程中编程的:
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]
与
其他一些建议:
RaiseLastError
(或者实际上是RaiseLastError
),以便GetLastError
可以检索所需API调用的错误代码。您在设置标签标题后调用它,这很可能导致意外调用SetLastError
DoTerminate
方法中设置事件。这样,您就不会从析构函数调用FEvent
上的方法。如果构造函数引发,则FEvent
可以是nil
Terminate
时,显然没有遵守这一点。您需要决定是使用free-on-terminate,还是保留对线程的引用。你必须选择一个或另一个选项。你不能两全其美
如果希望在终止线程时使用free,那么需要使用单独的事件来表示取消。将该事件传递给构造上的线程。测试设置的事件,而不是测试thread方法中是否终止了
。设置要取消的事件
然而,这一切都很奇怪。您说您希望在终止线程时使用free,但是您还希望等待所有线程完成。使用free-on-terminate线程会迫使您创建额外的事件来处理等待和取消。因为您无法保留对线程的引用。现在,您必须释放您创建的事件。因此,您避免了释放线程,而是将其替换为释放事件的要求
您的方法比需要的复杂得多。我认为你应该:
- 停止使用自由终止线程
- 删除所有事件
- 等待线程句柄
- 等待完成后释放线程
仅在不需要等待线程时才使用免费终止线程。我不知道是否需要SSCCE。如果需要,我可以发布。不,真的没有必要保留
FThreads
。你打算用它们做什么?你永远不能提及他们。事实上,你保留了这些参考资料,并告诉我你不能删除它们,这告诉我还有更多的事情你没有告诉我们。我不知道你为什么不做SSCCE。你不能调用FThreads[x]。在FreeOnTerminate
线程上终止。如果你需要提前停止线程,你必须有一个同步对象来完成,比如TSimpleEvent。。。。或将FreeOnTerminate设置为false。问题发生在线程已经完成并在调用Terminate时释放时。我相信这是SSCCE中的一个输入错误,但无论如何我会检查。不,这就是问题所在。。。我不相信在我写答案的时候我甚至没有看到TOndrej的评论。但是,是的,这是对Ondrej的支持。关于最后一点,代码只使用这种方式来终止线程。你是说我应该只在线程自行终止时使用FreeOnTerminated?我在答案的末尾添加了更多的文本。基本上,我认为你需要停止使用免费终止。因为您正在等待线程。
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]