Between the time the TThread instance was created and started, the main thread will continue to execute the code. If the code in the main thread depends on the current thread, which should be fully started, it must wait until the Execute process begins.
Consider the following code:
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;
Without a loop waiting for the IsRunning flag, DoAction will not be able to successfully send the message to the contained window handle because it has not yet been created. Basically, inc(Test) inside WndProc will not start.
Is there a better way to wait for the thread to start and complete the necessary initialization inside the Execute method, or is this solution as good as it gets?
Note. I know that AllocateHWnd and DeallocateHWnd are not thread safe and should not be used in production code, as shown above.
source share