没有窗口的新创建的线程的“窗口过程”

时间:2009-11-13 06:40:19

标签: multithreading winapi

我想为某些db写入创建一个线程,如果不存在db,则不应该阻止ui。为了与主线程同步,我想使用Windows消息。主线程将数据发送到写入线程。

发送没问题,因为CreateThread返回新创建的线程的句柄。我考虑过创建一个标准的Windows事件循环来处理消息。但是如何在没有窗口的情况下将窗口过程作为DispatchMessage的目标?

标准的Windows事件循环(来自MSDN):

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

为什么是Windows消息?因为它们很快(Windows依赖它们)和线程安全。这种情况也很特殊,因为第二个线程不需要读取任何数据。它只需要接收数据,将其写入数据库,然后等待下一个数据到达。但这正是标准事件循环的作用。 GetMessage等待数据,然后处理数据并重新开始。甚至还有一个用于终止线程的定义信号 - WM_QUIT。

其他同步构造偶尔会阻塞其中一个线程(临界区,信号量,互斥锁)。至于评论中提到的事件 - 我不知道。

3 个答案:

答案 0 :(得分:1)

这似乎与常识相反,但对于没有窗口的消息,实际上最好使用窗口proc创建一个隐藏窗口,而不是在消息泵中手动过滤GetMessage()的结果。

你有一个HWND的事实意味着只要正确的线程有一个消息泵,消息就会在某个地方被路由。考虑到许多函数,甚至是内部Win32函数,都有自己的消息泵(例如MessageBox())。并且MessageBox()的代码不会知道在GetMessage()之后调用您的自定义代码,除非有DispatchMessage()知道的窗口句柄和窗口过程。

通过创建隐藏窗口,您的线程中运行的任何消息泵都会覆盖您,即使它不是由您编写的。

编辑:但不要只听我的话,请查看微软的Raymond Chen的这些文章。

答案 1 :(得分:1)

注意:仅当您不需要任何与UI相关或与COM相关的代码时,请参阅此代码。除了这样的极端情况,这段代码正常工作:特别适用于纯计算限制的工作线程。

如果线程没有窗口,则不需要

DispathMessageTranslateMessage。所以,只需忽略它。 HWND与您的方案无关。实际上你根本不需要创建任何Window。请注意,需要两个* Message函数来处理与Windows-UI相关的消息,例如WM_KEYDOWN和WM_PAINT。

我还希望Windows消息使用PostThreadMessageGetMessagePeekMessage在线程之间进行同步和通信。我想从我的代码剪切和粘贴,但我只是简单地勾勒出这个想法。

#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)
{
  ...
}

我有点害怕这就是你想要的。但是,我认为代码很容易理解。通常,创建一个没有消息队列的线程。但是,一旦调用了与Window-message相关的函数,就会初始化该线程的消息队列。请注意,再次发布/接收窗口消息不需要窗口。

答案 2 :(得分:0)

除非线程具有要管理的实际窗口,否则您的线程中不需要窗口过程。一旦线程调用了Peek / GetMessage(),它就会有一个窗口过程会收到的相同消息,因此可以立即对其进行操作。仅在涉及实际窗口时才需要调度消息。如果您的线程使用的其他对象在内部具有自己的窗口(例如,ActiveX / COM),则最好调度您不关心的任何消息。例如:

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;
        }
    }
}