Delphi 等待TThread实例启动的正确方法是什么
在Delphi 等待TThread实例启动的正确方法是什么,delphi,tthread,Delphi,Tthread,在TThread实例创建和启动之间,主线程将继续执行代码。若主线程中的代码依赖于要完全启动和运行的线程,那个么它必须以某种方式等待线程Execute方法实际启动 考虑以下代码: const WM_MY_ACTION = WM_APP + 10; type TWndThread = class(TThread) protected fWndHandle: THandle; IsRunning: boolean; procedure WndProc(var Ms
TThread
实例创建和启动之间,主线程将继续执行代码。若主线程中的代码依赖于要完全启动和运行的线程,那个么它必须以某种方式等待线程Execute
方法实际启动
考虑以下代码:
const
WM_MY_ACTION = WM_APP + 10;
type
TWndThread = class(TThread)
protected
fWndHandle: THandle;
IsRunning: boolean;
procedure WndProc(var Msg: TMessage);
procedure Execute; override;
public
Test: integer;
procedure AfterConstruction; override;
procedure DoAction;
end;
procedure TWndThread.AfterConstruction;
begin
inherited;
while not IsRunning do Sleep(100); // wait for thread start up
end;
procedure TWndThread.Execute;
var
Msg: TMsg;
begin
fWndHandle := AllocateHWnd(WndProc);
IsRunning := true;
try
while not Terminated do
begin
if MsgWaitForMultipleObjects(0, nil^, False, 1000, QS_ALLINPUT) = WAIT_OBJECT_0 then
begin
while PeekMessage(Msg, 0, 0, 0, PM_REMOVE) do
begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
end;
end;
finally
DeallocateHWnd(fWndHandle);
end;
end;
procedure TWndThread.WndProc(var Msg: TMessage);
begin
case Msg.Msg of
WM_MY_ACTION:
begin
inc(Test);
end;
else Msg.Result := DefWindowProc(fWndHandle, Msg.Msg, Msg.WParam, Msg.LParam);
end;
end;
procedure TWndThread.DoAction;
begin
PostMessage(fWndHandle, WM_MY_ACTION, 0, 0);
end;
var
t: TWndThread;
begin
t := TWndThread.Create;
t.DoAction;
t.Terminate;
end;
如果没有等待IsRunning
标志的循环,DoAction
将无法成功地将消息发布到包含的窗口句柄,因为它尚未创建。基本上,inc(Test)
内部WndProc
不会被触发
在Execute
方法中,是否有更好的方法来等待线程启动并完成必要的初始化,或者这个解决方案是否达到了预期效果
注意:我知道
AllocateHWnd
和DeallocateHWnd
不是线程安全的,不应该像上面的示例那样在生产代码中使用。主线程
TSimpleEvent
将满足您的需要TSimpleEvent
,这是对ResetEvent
的调用。我希望一个新制作的t简单事件会处于无信号状态,但在我脑海里我记不起那个细节
TSimpleEvent
,这意味着调用WaitFor
TSimpleEvent
,这意味着调用SetEvent
正如David所指出的,一个
TEvent
可以实现这一点,其他任何数量的同步对象也可以。举个例子(反正我已经写完了):
将
FIsRunning
从Boolean
更改为TEvent
,以便在一切就绪时收到信号
现在,您可以随时等待此事件(尤其是在公共方法中,如DoAction
):
我想我明白你的意思了:
uses
Windows, SysUtils, Classes;
type
TMyThread = class(TThread)
private
FStartEvent: THandle;
protected
procedure Execute; override;
public
procedure AfterConstruction; override;
end;
implementation
{ TMyThread }
procedure TMyThread.AfterConstruction;
begin
FStartEvent:= CreateEvent(nil, True, False, nil);
inherited; // this starts the thread
if WaitForSingleObject(FStartEvent, INFINITE) = WAIT_FAILED
// means the thread finished;
// should not happen but you can check it to be safe
then ..;
// otherwise nothing should be done
end;
procedure TMyThread.Execute;
begin
SetEvent(FStartEvent);
// ...
CloseHandle(FStartEvent);
end;
或者您可以按照鲁福爵士的建议,将WaitForSingleObject
从AfterConstruction
移动到您的DoAction
代码:
procedure TMyThread.CheckRunning;
begin
if WaitForSingleObject(FStartEvent, INFINITE) = WAIT_FAILED
then // the thread already finished;
// this should not normally happen,
// maybe application is terminating or something else unexpected.
// ..;
// else the thread is up and running here.
end;
我编辑了问题-将等待代码移动到
AfterConstruction
,以使线程类更加独立。我想没有什么能阻止我以一种内敛的方式使用TSimpleEvent
,也是吗?等待的最佳场所是DoAction
方法或任何其他公共方法,在这些方法中,您必须确保一切正常running@SirRufo是正确的,只有在需要等待的时候才等待是更好的,只要想想是否每个线程都需要2秒钟才能开始运行。仅创建10个线程将阻塞20秒。看看我的答案,没有人会等到行动的时候
var
t: TWndThread;
begin
t := TWndThread.Create;
try
t.DoAction;
finally
t.Free;
end;
end;
uses
Windows, SysUtils, Classes;
type
TMyThread = class(TThread)
private
FStartEvent: THandle;
protected
procedure Execute; override;
public
procedure AfterConstruction; override;
end;
implementation
{ TMyThread }
procedure TMyThread.AfterConstruction;
begin
FStartEvent:= CreateEvent(nil, True, False, nil);
inherited; // this starts the thread
if WaitForSingleObject(FStartEvent, INFINITE) = WAIT_FAILED
// means the thread finished;
// should not happen but you can check it to be safe
then ..;
// otherwise nothing should be done
end;
procedure TMyThread.Execute;
begin
SetEvent(FStartEvent);
// ...
CloseHandle(FStartEvent);
end;
procedure TMyThread.CheckRunning;
begin
if WaitForSingleObject(FStartEvent, INFINITE) = WAIT_FAILED
then // the thread already finished;
// this should not normally happen,
// maybe application is terminating or something else unexpected.
// ..;
// else the thread is up and running here.
end;