Jump to content
3ddark

MsgWaitForMultipleObjects Usage

Recommended Posts

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 by 3ddark

Share this post


Link to post

This doesn't answer your question, but using Memo1 from the thread is not allowed. VCL is not thread safe.

 

  • Like 1

Share this post


Link to post

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
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 by FPiette
Added some advices.
  • Like 1

Share this post


Link to post
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
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 by FPiette

Share this post


Link to post

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.

  • Like 1

Share this post


Link to post
4 minutes ago, FPiette said:

Sure I did since the poster (You !)

  1. I'm not the OP.
  2. My example with MsgWaitForMulpitleObjects used QS_PAINT and DispatchMessage.
  3. The OP's use of QS_ALLINPUT is probably based on not understanding the problems associated with it.
  4. 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
15 minutes ago, Anders Melander said:
  1. 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

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;

 

  • Like 4

Share this post


Link to post

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

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×