views:

232

answers:

3

Hi,

I want to create a thread for some db writes that should not block the ui in case the db is not there. For synchronizing with the main thread, I'd like to use windows messages. The main thread sends the data to be written to the writer thread.

Sending is no problem, since CreateThread returns the handle of the newly created thread. I thought about creating a standard windows event loop for processing the messages. But how do I get a window procedure as a target for DispatchMessage without a window?

Standard windows event loop (from MSDN):

while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
{ 
    if (bRet == -1)
    {
        // handle the error and possibly exit
    }
    else
    {
        TranslateMessage(&msg); 
        DispatchMessage(&msg); 
    }
}


Why windows messages? Because they are fast (windows relies on them) and thread-safe. This case is also special as there is no need for the second thread to read any data. It just has to recieve data, write it to the DB and then wait for the next data to arrive. But that's just what the standard event loop does. GetMessage waits for the data, then the data is processed and everything starts again. There's even a defined signal for terminating the thread that is well understood - WM_QUIT.

Other synchronizing constructs block one of the threads every now and then (critical section, semaphore, mutex). As for the events mentioned in the comment - I don't know them.

A: 

You don't need a window procedure in your thread unless the thread has actual windows to manage. Once the thread has called Peek/GetMessage(), it already has the same message that a window procedure would receive, and thus can act on it immediately. Dispatching the message is only necessary when actual windows are involved. It is a good idea to dispatch any messages that you do not care about, in case other objects used by your thread have their own windows internally (ActiveX/COM does, for instance). For example:

while( (bRet = GetMessage(&msg, NULL, 0, 0)) != 0 )
{
    if (bRet == -1)
    {
      // handle the error and possibly exit
    }
    else
    {
        switch( msg.message )
        {
            case ...: // process a message
                ...
                break;
            case ...: // process a message
                ...
                break;
            default: // everything else
                TranslateMessage(&msg);
                DispatchMessage(&msg);
                break;
        }
    }
}
Remy Lebeau - TeamB
+1  A: 

It might seem contrary to common sense, but for messages that don't have windows, it's actually better to create a hidden window with your window proc than to manually filter the results of GetMessage() in a message pump.

The fact that you have an HWND means that as long as the right thread has a message pump going, the message is going to get routed somewhere. Consider that many functions, even internal Win32 ones, have their own message pumps (for example MessageBox()). And the code for MessageBox() isn't going to know to invoke your custom code after its GetMessage(), unless there's a window handle and window proc that DispatchMessage() will know about.

By creating a hidden window, you're covered by any message pump running in your thread, even if it isn't written by you.

EDIT: but don't just take my word for it, check these articles from Microsoft's Raymond Chen.

asveikau
Isn't the handle returned by CreateThread a HWND?
Tobias Langner
no, it's a HANDLE. a thread handle to be specific. you can use it to "join" the thread via WaitForSingleObject() etc., and query the return value of the thread routine... you can also use it to send messages to, but that relies on there being a message pump that will process it, which has the weaknesses I describe.
asveikau
Thank you very much.
Tobias Langner
You cannot use the thread's HANDLE to post messages to the thread. You have to use the thread's ID instead, which is also returned by CreateThread(). PostThreadMessage() takes the ID, not the HANDLE, as input.
Remy Lebeau - TeamB
As with all of RCs advice (And advice in general) its important to understand the context of the advice (as RC points out).So, if you are designing a non UI worker thread, and then decide to use thread messages to serialize comms to that thread, well: it's not inappropriate to use PostThreadMessage. The design has already precluded the contra-indications RC mentions. The only modal message loops you will likely encounter be aware of would be created by ASSERT macro's showing modal dialogs. (But, frankly modal assert dialogs cause so many re-entrancey problems in a regular UI thread anyway!)
Chris Becke
+1  A: 

NOTE: Refer this code only when you don't need any sort of UI-related or some COM-related code. Other than such corner cases, this code works correctly: especially good for pure computation-bounded worker thread.

DispathMessage and TranslateMessage are not necessary if the thread is not having a window. So, simply just ignore it. HWND is nothing to do with your scenario. You don't actually need to create any Window at all. Note that that two *Message functions are needed to handle Windows-UI-related message such as WM_KEYDOWN and WM_PAINT.

I also prefer Windows Messages to synchronize and communicate between threads by using PostThreadMessage and GetMessage, or PeekMessage. I wanted to cut and paste from my code, but I'll just briefly sketch the idea.

#define WM_MY_THREAD_MESSAGE_X   (WM_USER + 100)
#define WM_MY_THREAD_MESSAGE_Y   (WM_USER + 100)


// Worker Thread: No Window in this thread
unsigned int CALLBACK WorkerThread(void* data)
{
  // Get the master thread's ID
  DWORD master_tid = ...;
  while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
  { 
    if (bRet == -1)
    {
      // handle the error and possibly exit
    }
    else
    {
      if (msg.message == WM_MY_THREAD_MESSAGE_X)
      {
        // Do your task

        // If you want to response,
        PostThreadMessage(master_tid, WM_MY_THREAD_MESSAGE_X, ... ...);
      }

      //...
      if (msg.message == WM_QUIT)
        break;
    }
  }
  return 0;
}


// In the Master Thread
//
// Spawn the worker thread
CreateThread( ... WorkerThread ... &worker_tid);

// Send message to worker thread
PostThreadMessage(worker_tid, WM_MY_THREAD_MESSAGE_X, ... ...);

// If you want the worker thread to quit
PostQuitMessage(worker_tid);

// If you want to receive message from the worker thread, it's simple
// You just need to write a message handler for WM_MY_THREAD_MESSAGE_X
LRESULT OnMyThreadMessage(WPARAM, LPARAM)
{
  ...
}

I'm a bit afraid that this is what you wanted. But, the code, I think, is very easy to understand. In general, a thread is created without having message queue. But, once Window-message related function is called, then the message queue for the thread is initialized. Please note that again no Window is necessary to post/receive Window messages.

minjang
According to one of the articles I cited in my answer, it's not just GUI code that has modal message pumps. COM synchronization also does. further, just because you don't create windows doesn't mean that the same is true of all functions you call, including those you don't control. best to code defensively and use the hidden window technique.
asveikau
I already had read Chen's articles. But, in my experience, it was very rare that COM-related modal loop eats messages posted by `PostThreadMessage`. But, you are technically right. I'll edit the code.
minjang
You wrote a handler for your new user message, OnMyThreadMessage(). But where is it called, how is it registered as a callback (especially if the message pump is not explicitely in your code, e.g. using an event loop from an application framework)?
Nikola Gedelovski