我正在使用的应用程序中几乎没有线程。 这些线程设置为 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]
其他一些建议:
- 您必须立即调用
RaiseLastWin32Error
(或者实际上是RaiseLastOSError
),以便GetLastError
可以检索所需 API 调用的错误代码。您在设置标签标题后调用它,这很可能会导致意外调用SetLastError
。 - 最好在重写的
DoTerminate
方法中设置事件。这样您就不会从析构函数调用FEvent
上的方法。如果构造函数引发,则FEvent
可以为nil
。 - 您不应保留对自由终止线程的引用。由于线程对象可以随时释放,因此您很容易最终持有对已销毁对象的引用。
最后一点非常重要。当您循环调用 Terminate
的所有线程时,您显然没有遵守这一点。您需要决定是否使用 free-on-terminate,或者是否要保留对线程的引用。您必须选择一个或其他选项。你不能两全其美。
如果您希望使用终止时释放线程,则需要使用单独的事件来发出取消信号。将该事件传递给构造线程。不要在线程方法中测试 Termied
,而是测试正在设置的事件。设置您希望取消的事件。
然而,这一切都很奇怪。您说您希望使用 free-on-terminate 线程,但您也想等待所有线程完成。使用终止时释放线程会迫使您创建额外的事件来处理等待和取消。因为你无法保留对线程的引用。现在,您必须释放您创建的事件。因此,您避免了必须释放线程,而是用释放事件的要求来代替它。
您的方法比需要的复杂得多。在我看来你应该:
- 停止使用 free-on-terminate 线程。
- 删除所有事件。
- 等待线程句柄。
- 等待完成后释放线程。
仅当不需要等待线程时才使用 free-on-terminate 线程。
关于windows - 如何等待多个事件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26216568/