当一个线程第一次被创建时,系统假定线程不会用于任何与用户相关的任务。这样可以减少线程对系统资源的要求。但是,一旦该线程调用一个与图形用户界面有关 的函数 ( 如检查它的消息队列或建立一个窗口 ),系统就会为该线程分配一些另外的资源,以便它能够执行与用户界面有关的任务。特别是,系统分配了一个THREADINFO结构,并将这个数据结构与线 程联系起来。
THREADINFO结构体如下:
线程为窗口维护一个threadinfo结构,
threadinfo数据结构:
------------------
|登记消息队列指针|
------------------
|虚拟输入队列指针|
------------------
|发送消息队列指针|
------------------
|应答消息队列指针|
------------------
| nExitCode |
------------------
| 唤醒标志 |
------------------
|局部输入状态变量|
------------------
1.将消息发送到线程的消息队列
当线程有了与之联系的THREADINFO结构时,消息就有自己的消息队列集合。
通过调用函数 BOOL PostMesssage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
可以将消息放置在线程的登记消息队列中。
当一个线程调用这个函数时,系统要确定是哪个线程建立了用 hwnd 参数标识的窗口。然后系统分配一块内存,将这个消息参数存储在这块内存中,并将这块内存增加到相应线程的登记消息队列中。并且该函数还设置QS_POSTMESSAGE唤醒位。函数 PostMesssage 在 登记了消息后立即返回, 调用该函数的线程不知道登记的消息是否被指定窗口的窗口过程所处理。
还可通过调用函数 BOOL PostThreadMesssage(DWORD dwThreadId, UINT uMsg, WPARAM wParam, LPARAM lParam) 将消息放置在线程的登记消息队列中,同 PostMesssage 函数一样,该函数在向线程的队列登记消息后立即返回 ,调用该函数的线程不知道消息是否被处理。
向线程的队列发送消息的函数还有 VOID PostQuitMesssage(int nExitCode) ;
该函数可以终止线程消息的循环,调用该函数类似于调用:PostThreadMesssage(GetCurrenThreadId( ), WM_QUIT, nExitCode, 0); 但 PostQuitMesssage 并不实际登记一个消息到任何队列中。只是在内部,该函数设定 QS_QUIT 唤醒标志,并设置 THREADINFO 结构的 nExitCode 成员。
2.向窗口发送消息
将窗口消息直接发送给一个窗口过程可以使用函数 LRESULT SendMessage( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 窗口过程将处理这个消息,只有当消息被处理后,该函数才能返回。即具有同步的特性。
该函数的工作机制:
2.1 如果调用该函数的线程向该线程所建立的窗口发送了一个消息,SendMessage 就很简单:它只是调用指定窗口的窗口过程,将其作为一个子例程。当窗口过程完成对消息的处理时,它向 SendMessage 返回一个值。SendMessage 再将这个值返回给调用线程。
2.2 当 一个线程向其他线程所建立的窗口发送消息时,SendMessage 就复杂很多(即使两个线程在同一个进程中也是如此)。windows 要求建立窗口的线程处理窗口的消息。所以当一个线程调用 SendMessage 向一个由其他进程所建立的窗口发送一个消息,也就是向其他线程发送消息,发送线程不可能处理该窗口消息,因为发送线程不是运行 在接收进程的地址空间中,因此不能访问相应窗口的过程的代码和数据。(对于这个,我有点疑问:同一个进程的不同线程是运行在相同进程的地址空间中,它也采用这种机制,又作何解释呢?) 实际上,发送线程要挂起,而有另外的线程处理消息。所以为了向其他线程建立的窗口发送一个窗口消息,系统必须执行一些复杂的动作。
由于windows使用上述方法处理线程之间的发送消息,所以有可能造成线程挂起,严重的会出现死锁。
利用一下4个函数可以编写保护性代码防护出现这种情况。
1. LRESULT SendMessageTimeout( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT fuFlags, UINT uTimeout , PDWORD_PTR pdwResult);
2. BOOL SendMessageCallback( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, SENDSYNCPROC pfnResultCallback, ULONG_PTR dwData);
3. BOOL SendNotifyMessage( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
4. BOOL ReplyMessage( LRESULT lResult);
另外可以使用函数 BOOL InSendMessage( ) 判断是在处理线程间的消息发送,还是在处理线程内的消息发送
==================================
typedef struct tagTHREADINFO {
W32THREAD;
//***************************************** begin: USER specific fields
LIST_ENTRY PtiLink; // Link to other threads on desktop
PTL ptl; // Listhead for thread lock list
PTL ptlOb; // Listhead for kernel object thread lock list
PTL ptlPool; // Listhead for temp pool usage
int cEnterCount;
struct tagPROCESSINFO *ppi; // process info struct for this thread
struct tagQ *pq; // keyboard and mouse input queue
PKL spklActive; // active keyboard layout for this thread
MLIST mlPost; // posted message list.
USHORT fsChangeBitsRemoved;// Bits removed during PeekMessage
USHORT cDeskClient; // Ref count for CSRSS desktop
PCLIENTTHREADINFO pcti; // Info that must be visible from client
CLIENTTHREADINFO cti; // Use this when no desktop is available
HANDLE hEventQueueClient;
PKEVENT pEventQueueServer;
PKEVENT *apEvent; // Wait array for xxxPollAndWaitForSingleObject
PDESKTOP rpdesk;
HDESK hdesk; // Desktop handle
ACCESS_MASK amdesk; // Granted desktop access
PDESKTOPINFO pDeskInfo; // Desktop info visible to client
PCLIENTINFO pClientInfo; // Client info stored in TEB
DWORD TIF_flags; // TIF_ flags go here.
PUNICODE_STRING pstrAppName; // Application module name.
struct tagSMS *psmsSent; // Most recent SMS this thread has sent
struct tagSMS *psmsCurrent; // Received SMS this thread is currently processing
struct tagSMS *psmsReceiveList; // SMSs to be processed
LONG timeLast; // Time, position, and ID of last message
POINT ptLast;
DWORD idLast;
int cQuit;
int exitCode;
int cPaintsReady;
UINT cTimersReady;
PMENUSTATE pMenuState;
union {
PTDB ptdb; // Win16Task Schedule data for WOW thread
PWINDOWSTATION pwinsta; // Window station for SYSTEM thread
PDESKTOP pdeskClient; // Desktop for CSRSS thread
};
PSVR_INSTANCE_INFO psiiList; // thread DDEML instance list
DWORD dwExpWinVer;
DWORD dwCompatFlags; // The Win 3.1 Compat flags
UINT cWindows; // Number of windows owned by this thread
UINT cVisWindows; // Number of visible windows on this thread
struct tagQ *pqAttach; // calculation variabled used in
// AttachThreadInput()
int iCursorLevel; // keep track of each thread's level
DWORD fsReserveKeys; // Keys that must be sent to the active
// active console window.
struct tagTHREADINFO *ptiSibling; // pointer to sibling thread info
PMOVESIZEDATA pmsd;
DWORD fsHooks; // WHF_ Flags for which hooks are installed
PHOOK asphkStart[CWINHOOKS]; // Hooks registered for this thread
PHOOK sphkCurrent; // Hook this thread is currently processing
PSBTRACK pSBTrack;
#ifdef FE_IME
PWND spwndDefaultIme; // Default IME Window for this thread
PIMC spDefaultImc; // Default input context for this thread
HKL hklPrev; // Previous active keyboard layout
#endif
} THREADINFO;
================================================
http://www.cnblogs.com/ahuo/archive/2007/08/22/864948.html
把 window 线程间传送消息仔细的看了一遍,觉得以前的理解很不深刻。说一说对 PostThreadMessage 的理解。
PostThreadMessage 是一个线程体发送一个消息到指定的线程 ID ,其原型如下:
BOOL PostThreadMessage(
DWORD idThread,
UINT Msg,
WPARAM wParam,
LPARAM lParam
);
这个函数既可以发送消息给工作线程,也可以发送给 UI 线程。接受 PostThreadMessage 的线程必须已经有了一个 message queue ,否则调用 PostThreadMessage 会失败。因为此原因使用 GetLastError 会得到错误码为 1444 ,这种情况经常出现,解决方法有如下两种:
1. 调用 PostThreadMessage ,如果失败,就 Sleep 一段时间再次调用 PostThreadMessage 直到调用成功;
2. 创建一个 Event 对象,让 PostThreadMessage 等待接受的线程创建一个 message queue 。可以通过调用 PeekMessage 强制系统创建一个 message queue 。示例代码如下:
假设 mainAPP 是发送线程 ThreadA 是接受线程
/*mainAPP.cpp*/
……
hStartEvent = ::CreateEvent(0,FALSE,FALSE,0); //create thread start event
if(hStartEvent == 0)
{
printf("create start event failed,errno:%d/n",::GetLastError());
return 1;
}
::WaitForSingleObject(hStartEvent,INFINITE);
CloseHandle(hStartEvent);
if(!PostThreadMessage(threadaID, WM_MESSAGE_A,0,0))
{
_tprintf(_T("post error! %d/n"), GetLastError());
return 1;
}
…
ThreadA 是接受线程
/* ThreadA */
MSG msg;
PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
if(!SetEvent(hStartEvent))
{
printf("set event error,%d/n",GetLastError());
return 1;
}
while(true){
if(GetMessage(&msg, 0,0,0))
{
switch(msg.message){
case WM_MESSAGE_A:
……
break;
}
}
}
}
PostThreadMessage 传递的消息如果要包含信息,要注意在结束的时候释放消息中的信息。在消息中附加信息方法如下
/*构造信息如下*/
char* pInfo = new char[MAX_INFO_SIZE]; //create dynamic msg
sprintf(pInfo,"msg_%d",++count);
PostThreadMessage(nThreadID,MY_MSG,(WPARAM)pInfo,0)//post thread msg
/*
解释信息如下*/
if(GetMessage(&msg,0,0,0)) //get msg from message queue
{
switch(msg.message)
{
case MY_MSG:
char * pInfo = (char *)msg.wParam;
printf("recv %s/n",pInfo);
delete[] pInfo;
//这里释放了资源
break;
}
}
做了一个简单的消息通信实验,让主线程中等待用户输入,产生不同的消息,并把这些消息 post 给子线程,子线程根据产生的消息做出不同的反映。这些子线程可以是工作线程也可以是 UI 线程。
#include < windows.h > #include < cstdio > #include < process.h > #define MY_MSG WM_USER+100 const int MAX_INFO_SIZE = 20 ; HANDLE hStartEvent; // thread start event // thread function unsigned __stdcall fun( void * param) { printf( " thread fun start ![]() MSG msg; PeekMessage( & msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); if ( ! SetEvent(hStartEvent)) // set thread start event { printf( " set start event failed,errno:%d/n " ,::GetLastError()); return 1 ; } while ( true ) { if (GetMessage( & msg, 0 , 0 , 0 )) // get msg from message queue { switch (msg.message) { case MY_MSG: char * pInfo = ( char * )msg.wParam; printf( " recv %s/n " ,pInfo); delete[] pInfo; break ; } } }; return 0 ; } int main() { HANDLE hThread; unsigned nThreadID; hStartEvent = ::CreateEvent( 0 ,FALSE,FALSE, 0 ); // create thread start event if (hStartEvent == 0 ) { printf( " create start event failed,errno:%d/n " ,::GetLastError()); return 1 ; } // start thread hThread = (HANDLE)_beginthreadex( NULL, 0 , & fun, NULL, 0 , & nThreadID ); if (hThread == 0 ) { printf( " start thread failed,errno:%d/n " ,::GetLastError()); CloseHandle(hStartEvent); return 1 ; } // wait thread start event to avoid PostThreadMessage return errno:1444 ::WaitForSingleObject(hStartEvent,INFINITE); CloseHandle(hStartEvent); int count = 0 ; while ( true ) { char * pInfo = new char [MAX_INFO_SIZE]; // create dynamic msg sprintf(pInfo, " msg_%d " , ++ count); if ( ! PostThreadMessage(nThreadID,MY_MSG,(WPARAM)pInfo, 0 )) // post thread msg { printf( " post message failed,errno:%d/n " ,::GetLastError()); delete[] pInfo; } ::Sleep( 1000 ); } CloseHandle(hThread); return 0 ; } |
要把SETTING 改为多线程的
Project->Settings->click C/C tab,
在Category 中选Code Generation, 然后在Use run-time libray 中选一个
Multithread 配置
##################################################
http://blog.csdn.net/shiff/archive/2007/12/05/1918963.aspx
使用PostThreadMessage 与线程进行大数据量的通讯
PostThreadMessage是一个线程体发送一个消息到指定的线程ID,其原型如下:
BOOL PostThreadMessage(
DWORD idThread,
UINT Msg,
WPARAM wParam,
LPARAM lParam
); 这个函数既可以发送消息给工作线程,也可以发送给UI线程
问题1:这个函数成功执行的前提是目标线程已经有了一个消息队列。否则函数会失败。 通过GetLastError()函数可以获得错误代码1444。
这个问题的解决方式有以下两个:
1 不断调用PostThreadMessage函数,直到函数成功。 这是因为当线程收到这样的函数以后,会自动创建消协队列。
2 创建Event对象,让PostThreadMessage等待目标线程创建一个消息队列。而目标线程可以通过调用PeekMessage强制系统创建一个message queue。
示例代码如下:
假设mainAPP是发送线程ThreadA是接受线程
/*mainAPP.cpp*/
……
hStartEvent = ::CreateEvent(0,FALSE,FALSE,0); //create thread start event
if(hStartEvent == 0)
{
printf("create start event failed,errno:%d/n",::GetLastError());
return 1;
}
::WaitForSingleObject(hStartEvent,INFINITE);
CloseHandle(hStartEvent);
if(!PostThreadMessage(threadaID, WM_MESSAGE_A,0,0))
{
_tprintf(_T("post error! %d/n"), GetLastError());
return 1;
}
……
ThreadA是接受线程
/* ThreadA */
MSG msg;
PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
if(!SetEvent(hStartEvent))
{
printf("set event error,%d/n",GetLastError());
return 1;
}
while(true){
if(GetMessage(&msg, 0,0,0)) {
switch(msg.message){
case WM_MESSAGE_A:
……
break;
}
}
}
}
问题2:消息不能包含大量数据。 解决如下
1 发送线程在堆中非配一个大量数据 2 消息中包含堆的指针,然后进行发送 3 目标线程序收到消息后,把发送线程序分配的堆释放。
示例程序如下:
/*构造信息如下*/
char* pInfo = new char[MAX_INFO_SIZE]; //发送线程在这里创建了一个堆
sprintf(pInfo,"msg_%d",++count);
PostThreadMessage(nThreadID,MY_MSG,(WPARAM)pInfo,0)//post thread msg
/*解释信息如下*/
if(GetMessage(&msg,0,0,0)) //get msg from message queue
{
switch(msg.message)
{
case MY_MSG:
char * pInfo = (char *)msg.wParam;
printf("recv %s/n",pInfo);
delete[] pInfo; //这里释放了发送线程里创建的堆
break;
}
}