我正在使用的应用程序中有很少的线程。 这些线程设置为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;
答案 0 :(得分:3)
您传递第二个等待句柄的地址而不是第一个等待句柄的地址。取代
@Handles[1]
与
@Handles[0]
其他一些建议:
RaiseLastWin32Error
(或确实RaiseLastOSError
),以便GetLastError
可以检索所需API调用的错误代码。您在设置标签标题后调用它,这很可能会导致偶然调用SetLastError
。DoTerminate
方法设置事件。这样你就不会从析构函数中调用FEvent
上的方法。如果构造函数引发,则FEvent
可以是nil
。最后一点非常重要。当你绕过调用Terminate
的所有线程时,你显然不遵守这一点。您需要决定是否使用free-on-terminate,或者是否要保留对线程的引用。你必须选择一个或其他选项。你无法双管齐下。
如果您希望使用免费终止线程,那么您需要使用单独的事件来发出取消信号。将该事件传递给构造中的线程。而不是在线程方法中测试Terminated
,而不是测试正在设置的事件。您希望取消时设置活动。
然而,所有这些都很奇怪。你说你希望使用free-on-terminate线程,但是你也想等待所有的线程完成。然后使用免费终止线程会强制您创建额外的事件来处理等待和取消。因为您无法保留对线程的引用。现在,您必须释放您创建的事件。因此,您已经避免了必须释放线程,但将其替换为释放事件的要求。
你的方法比它需要的要复杂得多。在我看来,你应该:
当您不需要等待线程时,只使用free-on-terminate线程。