3ddark 3 Posted September 15, 2020 (edited) How to wait for the above thread to finish before the main thread freezes? procedure TForm2.Button1Click(Sender: TObject); var Thrd : TThread; msg: tagMSG; Ret: Cardinal; begin Thrd := TThread.CreateAnonymousThread( procedure begin Sleep(10000); Memo1.Lines.Add('End of Thread'); end ); Thrd.Start; //The thread above is waiting for 10 seconds within itself. We will wait for the above thread to finish before the main thread freezes Ret := MsgWaitForMultipleObjects(0, Thrd, True, INFINITE, QS_ALLINPUT); //use with WaitMessage case Ret of WAIT_OBJECT_0: ; WAIT_OBJECT_0+1: ; WAIT_TIMEOUT: ; end; Memo1.Lines.Add('Thread finish'); end; Edited September 15, 2020 by 3ddark Share this post Link to post
FPiette 383 Posted September 15, 2020 This doesn't answer your question, but using Memo1 from the thread is not allowed. VCL is not thread safe. 1 Share this post Link to post
Anders Melander 1783 Posted September 15, 2020 I don't understand what you're asking about but you'll probably want to loop around MsgWaitForMultipleObjects - and process the messages somehow since you're using that variant of WaitForMultipleObjects. Don't fall into the trap of calling Application.ProcessMessages to handle the messages. I posted an example of MsgWaitForMultipleObjects yesterday: Share this post Link to post
3ddark 3 Posted September 15, 2020 @FPiette Add for loging I added deliberately Share this post Link to post
FPiette 383 Posted September 15, 2020 (edited) procedure TForm9.Button1Click(Sender: TObject); var Thrd : TThread; Ret : Cardinal; HandleArray : array [0..0] of THandle; begin Thrd := TThread.CreateAnonymousThread( procedure begin Sleep(5000); Memo1.Lines.Add('End of Thread'); // Not reliable! end ); Thrd.Start; HandleArray[0] := Thrd.Handle; // The thread above is waiting for 10 seconds within itself. // We will wait for the above thread to finish before the main thread freezes while TRUE do begin Ret := MsgWaitForMultipleObjects(1, HandleArray[0], FALSE, INFINITE, QS_ALLINPUT); if Ret = WAIT_OBJECT_0 then begin Memo1.Lines.Add('WAIT_OBJECT_0'); break; end; if Ret >= (WAIT_OBJECT_0 + 1) then begin Memo1.Lines.Add('WAIT_OBJECT_0+1'); Application.ProcessMessages; continue; end; if Ret = WAIT_TIMEOUT then begin Memo1.Lines.Add('WAIT_TIMEOUT'); break; end; Memo1.Lines.Add('Else') end; Memo1.Lines.Add('Thread finish'); end; Pay attention to change the access to Memo1 from the thread. Pay attention to re-entry issue. You should probably prevent it. Edited September 15, 2020 by FPiette Added some advices. 1 Share this post Link to post
Anders Melander 1783 Posted September 15, 2020 Just now, FPiette said: Application.ProcessMessages; Oh no, you didn't! 1 Share this post Link to post
FPiette 383 Posted September 15, 2020 9 minutes ago, 3ddark said: @FPiette Add for loging I added deliberately For logging, I find handy to use OutputDebugString. The messages are shown in the debugger log view. Share this post Link to post
FPiette 383 Posted September 15, 2020 (edited) 13 minutes ago, Anders Melander said: Oh no, you didn't! Sure I did since the poster (You !) want to process all inputs (QS_ALLINPUT). If you don't consume messages then MsgWaitForMulpitleObjects will immediately return WAIT_OBJECT_0 + 1. And if you don't process the messages, the main thread will be freeze and you asked to not freeze. Maybe I misunderstood your question. If this is the case, a better description is welcome. Edited September 15, 2020 by FPiette Share this post Link to post
Rollo62 536 Posted September 15, 2020 To put something in a thread and hold the main UI thread until its finished , that makes not much sense IMHO. Then you can also proceed everything in the main UI, its the same effect. A task should be running in parallel. 1 Share this post Link to post
Anders Melander 1783 Posted September 15, 2020 4 minutes ago, FPiette said: Sure I did since the poster (You !) I'm not the OP. My example with MsgWaitForMulpitleObjects used QS_PAINT and DispatchMessage. The OP's use of QS_ALLINPUT is probably based on not understanding the problems associated with it. The circumstances where it's safe to Application.ProcessMessages are so rare that one might just as well not use it. Your example uses it in a button click handler where it's definitely not safe. Share this post Link to post
FPiette 383 Posted September 15, 2020 15 minutes ago, Anders Melander said: The circumstances where it's safe to Application.ProcessMessages are so rare that one might just as well not use it. Your example uses it in a button click handler where it's definitely not safe. That's why I said in the message to prevent re-entry issue. as with many things, calling ProcessMessages is perfectly correct if you understand what happens when you do so. Share this post Link to post
Anders Melander 1783 Posted September 15, 2020 3 minutes ago, FPiette said: calling ProcessMessages is perfectly correct if you understand what happens when you do so 1 4 Share this post Link to post
Remy Lebeau 1394 Posted September 15, 2020 In this situation, it is best not to block the main thread at all. Disable the UI if you need to, but let the worker thread and main message queue run in parallel normally, and have the worker thread notify the main thread when it is finished, eg: procedure TForm2.Button1Click(Sender: TObject); var Thrd : TThread; begin Thrd := TThread.CreateAnonymousThread( procedure begin Sleep(10000); end ); Thrd.OnTerminate := ThreadFinished; Thrd.Start; // disable UI as needed ... end; procedure TForm2.ThreadFinished(Sender: TObject); begin Memo1.Lines.Add('Thread finished'); // enable UI as needed ... end; 4 Share this post Link to post
3ddark 3 Posted September 19, 2020 This is another solution unit MsgWaitForMultiple; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, System.SyncObjs; type TForm3 = class(TForm) Memo1: TMemo; Button1: TButton; procedure Button1Click(Sender: TObject); procedure FormCreate(Sender: TObject); private public Event: TEvent; end; var Form3: TForm3; implementation {$R *.dfm} procedure TForm3.Button1Click(Sender: TObject); var Thrd : TThread; begin Event.ResetEvent; Thrd := TThread.CreateAnonymousThread( procedure begin Sleep(10000); Event.SetEvent; end ); Thrd.Start; while Event.WaitFor(0) = wrTimeOut do begin WaitMessage; Application.ProcessMessages; end; Memo1.Lines.Add('Thread bitti'); end; procedure TForm3.FormCreate(Sender: TObject); begin Event := TEvent.Create(nil, True, False, ''); end; end. Share this post Link to post