【转】线程与消息队列

   当一个线程第一次被创建时,系统假定线程不会用于任何与用户相关的任务。这样可以减少线程对系统资源的要求。但是,一旦该线程调用一个与图形用户界面有关 的函数 ( 如检查它的消息队列或建立一个窗口 ),系统就会为该线程分配一些另外的资源,以便它能够执行与用户界面有关的任务。特别是,系统分配了一个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 /n " );

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


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值