在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;
Run Code Online (Sandbox Code Playgroud)
没有等待IsRunning标志的循环,DoAction将无法成功将消息发布到包含的窗口句柄,因为它尚未创建.基本上,inc(Test)内部WndProc不会被触发.
有没有更好的方法来等待线程启动并在Execute方法内完成必要的初始化,或者这个解决方案是否能够获得好处?
注意:我知道AllocateHWnd并且DeallocateHWnd不是线程安全的,不应该像上面的例子那样在生产代码中使用.
主线程
TSimpleEvent足以满足您的需求.TSimpleEvent这是一个电话ResetEvent.我希望新创造的TSimpleEvent会处于无信号状态,但是我不记得那个细节了.TSimpleEvent这意味着调用WaitFor.工人线程
TSimpleEvent这意味着调用SetEvent.如果一切准备就绪,请FIsRunning从更改Boolean为TEvent发出信号.
现在你可以在任何时候等待这个事件(特别是在公共方法中DoAction):
const
WM_MY_ACTION = WM_APP + 10;
type
TWndThread = class(TThread)
private
FIsRunning: TEvent; // <- event
protected
fWndHandle: THandle;
procedure WndProc(var Msg: TMessage);
procedure Execute; override;
procedure CheckIsRunning; // guard method
public
constructor Create;
destructor Destroy; override;
procedure DoAction;
end;
constructor TWndThread.Create;
begin
// create event
FIsRunning := TEvent.Create( nil, True, False, '' );
inherited;
end;
destructor Destroy;
begin
inherited;
// free event
FIsRunning.Free;
end;
procedure CheckIsRunning;
begin
// guard if terminated
if Terminated then
raise Exception.Create( 'Already terminated' );
// wait for event
FIsRunning.WaitFor();
end;
procedure TWndThread.Execute;
var
Msg: TMsg;
begin
fWndHandle := AllocateHWnd(WndProc);
// set event
FIsRunning.SetEvent;
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
// guard method
CheckIsRunning;
// do the action
PostMessage(fWndHandle, WM_MY_ACTION, 0, 0);
end;
Run Code Online (Sandbox Code Playgroud)
现在一切都很容易使用,你只需等待,如果有一个特殊的原因等待(访问DoAction方法太快)
var
t: TWndThread;
begin
t := TWndThread.Create;
try
t.DoAction;
finally
t.Free;
end;
end;
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2256 次 |
| 最近记录: |