我想为某些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。
其他同步构造偶尔会阻塞其中一个线程(临界区,信号量,互斥锁)。至于评论中提到的事件 - 我不知道。
答案 0 :(得分:1)
这似乎与常识相反,但对于没有窗口的消息,实际上最好使用窗口proc创建一个隐藏窗口,而不是在消息泵中手动过滤GetMessage()
的结果。
你有一个HWND
的事实意味着只要正确的线程有一个消息泵,消息就会在某个地方被路由。考虑到许多函数,甚至是内部Win32函数,都有自己的消息泵(例如MessageBox()
)。并且MessageBox()
的代码不会知道在GetMessage()
之后调用您的自定义代码,除非有DispatchMessage()
知道的窗口句柄和窗口过程。
通过创建隐藏窗口,您的线程中运行的任何消息泵都会覆盖您,即使它不是由您编写的。
编辑:但不要只听我的话,请查看微软的Raymond Chen的这些文章。
答案 1 :(得分:1)
注意:仅当您不需要任何与UI相关或与COM相关的代码时,请参阅此代码。除了这样的极端情况,这段代码正常工作:特别适用于纯计算限制的工作线程。
如果线程没有窗口,则不需要 DispathMessage
和TranslateMessage
。所以,只需忽略它。 HWND
与您的方案无关。实际上你根本不需要创建任何Window。请注意,需要两个* Message函数来处理与Windows-UI相关的消息,例如WM_KEYDOWN和WM_PAINT。
我还希望Windows消息使用PostThreadMessage
和GetMessage
或PeekMessage
在线程之间进行同步和通信。我想从我的代码剪切和粘贴,但我只是简单地勾勒出这个想法。
#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;
}
}
}