views:

610

answers:

3

There is a thread in my Delphi application that has a message-waiting loop. Every time it receives a message, it starts doing some work. Here is the execute procedure of that thread:

procedure TMyThread.Execute;
begin
  while GetMessage(Msg, 0, 0, 0) and not Terminated do
  begin
    {thread message}
    if Msg.hwnd = 0 then
    begin
      ...
    end
    else
      DispatchMessage(Msg);
  end;
end;

Doing some tests using my application I discovered that the GetMessage function is main thread dependent. By this I mean that while the main thread is doing some work, the GetMessage function in my thread does not return even though a message is waiting to be received by it (the message is sent by yet another thread using the PostThreadMessage funciton: PostMessage(MyThreadId, WM_MyMessage, 0, 0)).

Only when the main thread finishes its work or the Application.ProcessMessages method is called, does GetMessage return and my thread starts doing its work. Implementing this kind of inter-thread communication I was sure that my threads would work independently and I would never expect that the reception of messages sent directly to a thread would be dependent on the main thread.

Doing the tests I used the WaitForSingleObject function in the main thread, waiting for an event for a few seconds. This is when I noticed that my thread was not doing any work even though messages were sent to it by another thread. When the WaitForSingleObject function finally finished waiting and the main thread became idle, the GetMessage function in my thread returned.

Could somebody explain me why it works this way? Is there a workaround to that? I would like to make my thread receive messages independently. All my threads are created by the main thread. May this be the reason?

Thanks for your help in advance.

Mariusz.


Mghie, you were absolutely right again (you have helped me with the messaging stuff lately, you might remember). As you suggested, GetMessage returns immediately, but the thread hangs, in fact, on a call to a main window method:

procedure TMyThread.Execute;
begin
  while GetMessage(Msg, 0, 0, 0) and not Terminated do
  begin
    {thread message}
    if Msg.hwnd = 0 then
    begin
      ...
      if Assigned(FOnCommEventMethod) then
        FOnCommEventMethod(FCommEventsQueueItem);
      ...
    end
    else
      DispatchMessage(Msg);
  end;
end;

FOnCommEventMethod is a method of object, declared as 'procedure (EventMask: Cardinal) of object;' (this thread handles serial port events). In this case the FOnCommEventMethod was assigned a procedure belonging to the main form class. When the method is called by my thread, the thread hangs waiting for the main thread to finish its work.

How come? As you can see, I don't use the Synchronize() method to call this procedure. Therefore I would not expect my thread to be synchronizing with the main thread. Does it happen implicitly? BTW, I understand that any GUI components should not be accessed by any other threads but the main one, so the Synchronize method should be used, but I am only doing some quick tests now.

Comming back to the WaitForSingleObject subject, I know I should not use it, but it was only a test thanks to which (coincidentally) I noticed the problem.

Thanks for your help. If you did not help me, I would probably get rid of messaging and use events instead, and finally I would notice that it was not the reason :-).

A: 

See http://groups.google.com/group/borland.public.delphi.language.delphi.win32/browse_thread/thread/cf368834a606321b

Requested distillation, leaving off lots of gnarly details in which the devil hides: the main (or VCL) thread is special in Delphi because lots of things under the hood aren't thread safe, and it owns them all. Things like the message loop wind up waiting because of things that synchronize with the VCL thread under the hood. Many people have theories about this. The thing that seems to work the best is keep your VCL thread as light / responsive as possible to minimize waits. Everyone pretty much agrees on this. Some people have ideas about other things that might work but other people think they are just asking for trouble.

MarkusQ
There are 27 posts in that thread, many of them long. Can you distill that a little?
Rob Kennedy
+1  A: 

You can create a message queue for your thread. Just execute the following code in your thread:

MSG msg;
PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
SetEvent(messageQueueReady);
while (GetMessage(&msg, NULL, 0, 0)) 
{
    ...// do message processing here
}

The call to PeekMessage forces the OS to create a new message queue for your thread. You have to make sure to synchronize this, i.e. you have to wait (e.g. via WaitForSingleObject) for the call to succeed before you post messages to this thread (e.g. via PostThreadMessage). This is why there's a call to the SetEvent API in the example above.

Edit: Sorry for the example being in C, hope that's ok for you.

jn_
Using WaitForSingleObject() on the event handle is the more elaborate way, checking for success, sleeping and reposting the message works just as well. See http://msdn.microsoft.com/en-us/library/ms644946(VS.85).aspx
mghie
Yes of course you can sleep and poll, but why would you not use WaitForSingleObject? It's the cleanest way to wait for the queue to be created
jn_
+2  A: 

Only when the main thread finishes its work or the Application.ProcessMessages method is called, does GetMessage return and my thread starts doing its work.

I doubt that this is really what's happening. AFAIK the two message loops should be independent of each other, as long as you don't use other means of synchronizing the threads, like SendMessage(). Are you sure that the thread does indeed block inside of GetMessage() and not inside of Synchronize() (which is using SendMessage() internally)?

Doing the tests I used the WaitForSingleObject function in the main thread, waiting for an event for a few seconds.

You should never use WaitForSingleObject() in the main thread with a timeout longer than say 100 milliseconds, as this will make your GUI appear sluggish. In fact I would advise you to not use it at all in the main thread, because this is simply polling. Post a message from your worker thread instead.

mghie