• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            春暖花開
            雪化了,花開了,春天來了
            posts - 149,comments - 125,trackbacks - 0

            調(diào)用SendMessage 產(chǎn)生死鎖的問題分析

            http://m.shnenglu.com/woaidongmao/archive/2008/12/17/69696.html

             

             

            ()       SendMessage 的工作機制

            首先我要先簡要的說明一個和這個話題有關(guān)系的消息處理機制:

                Window操作系統(tǒng)當中,窗口時屬于所在Thread的也就是說 你這個窗口在那個Thread 當中Create 的那么你這個窗口就屬于那個Thread。同時窗口的消息處理函數(shù)也都會在這個Thread 當中被執(zhí)行的。(不要問為什么 Window 就是這么設(shè)計的 嘿嘿)

             

            在講死鎖之前我們先把SendMessage的工作機制搞清楚;

            SendMessage 發(fā)送出來的消息 到底進入不進入消息隊列,有人說進入,有人說不進入,其實都是錯誤的,確切的說是有時進入,有時不進入。那么什么時候進入,什么時候不進入呢? 我們舉一例子來說:假如在 Thread A 中有一個 窗口W1,那么 在 Thread A 中像 W1 SendMessage 一個消息,那么這個消息將不會被放入消息隊列,而是直接調(diào)用了W1的消息處理函數(shù)來直接處理了這個消息。這是不被放入隊列的情況;假如現(xiàn)在又多了一個Thread B ,那么在 Thread B 中 像 W1 SendMessage 發(fā)送消息 這個時候 W1 將被放入到 Thread A 的消息隊列當中,這些Thread A 中的消息循環(huán)的GetMessage Get到這個消息 并處理之。 這就是進入消息隊列情況;根據(jù)在哪里我們來看看我的測試結(jié)果:

             

            測試1我創(chuàng)建了一個無DOC/View 之支持的單文檔工程:

            我在CMainFrame添加如下代碼:

                    BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)

                           ON_WM_CREATE()

                           ON_WM_SETFOCUS()

                           ON_MESSAGE(WM_USER + 100,OnMy)

            ON_MESSAGE(WM_USER + 200,OnMy2)

            END_MESSAGE_MAP()

             

            LRESULT CMainFrame::OnMy(WPARAM wParam,LPARAM lParam)

            {

                         int i = 0;

                         return TRUE;

            }

             

            LRESULT CMainFrame::OnMy2(WPARAM wParam,LPARAM lParam)

            {

                         int i = 2;

                         return TRUE;

            }

            然后我再 int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) 的最后 加入了一行代碼:

            SendMessage(WM_USER + 100,0,0);

             

            此主題相關(guān)圖片如下:
            clip_image002

            然后直接 F5 運行程序 等到 程序停止在斷點上,我們看看Call Stack 的調(diào)用順序:


            clip_image001
            此主題相關(guān)圖片如下:
            clip_image003

             

            然后 我又將 SendMessage 改成:

            PostMessage(WM_USER + 100,0,0);

            然后直接 F5 運行程序 等到 程序停止在斷點上,我們再看看Call Stack 的調(diào)用順序:


            clip_image001
            此主題相關(guān)圖片如下:
            clip_image004

            通過這2 Call Stack 大家可以很清楚的看到,執(zhí)行SendMessage的時候,是直接調(diào)用了 AfxWndProcBase 這個 消息處理函數(shù)(MFC 通過HOOK 將所有窗口的處理函數(shù)都重定向到這個 函數(shù)上了,AfxWndProcBase()不明白的自己去看《MFC深入淺出》),大家可以很清楚的看到,在SendMessage AfxWindProcBase 之間根本沒有調(diào)用CWinApp::Run() ,也就是說從SendMessage 到執(zhí)行OnMy()根本沒有通過程序的主消息循環(huán)的GetMessage Run 內(nèi)部好像用的PeekMessage記不清楚了)取消息。那么有人會問,SendMessage的內(nèi)部就不會先發(fā)消息放入隊列再通過GetMesssage把消息取出來了嗎?答根本沒必要那樣做,那是脫褲子放P多此一舉。

            從這個測試例子的結(jié)果我判定SendMessage Thread A 中向 W1

            SendMessage 的消息根本不進入消息隊列。

            測試2那么什么時候進入隊列呢我來看看這個例子

            沿用上面那個例子的代碼我將 OnCreate 中的 SendMessage PostMessage 都刪除掉。然后加入如下代碼:

            //Thread Proc

            UINT ThreadProc(LPVOID lParam)

            {

                         CMainFrame * v_pFrameWnd = (CMainFrame *)lParam;

                         if(v_pFrameWnd)

                         {

                            v_pFrameWnd->SendMessage(WM_USER + 100,0,0);

                         }

                         return 0;

            }

            并且 在 OnCreate 種加入如下代碼:

            AfxBeginThread(ThreadProc,this);

            然后F5 運行 等待程序停在斷點處,看Call Stack 如下:


            clip_image001
            此主題相關(guān)圖片如下:
            clip_image005

            我們發(fā)現(xiàn)這個 Call Stack 就和剛才那個PostMessage Call Stack 是一樣的 這個 WM_USER + 100 消息是通過 Run 內(nèi)部的 GetMessage 取出來的 。所以我斷定:

             

            Thread B 中向W1 SendMessage 發(fā)送消息 ,消息是放入了 Thread A 的消息隊列中。由于SendMessage的特性只有當消息被執(zhí)行完畢才能夠返回,所以Thread B 中的SendMessage 要等 Thread A 當中消息執(zhí)行完畢后才能夠返回。

            ()       SendMessage 產(chǎn)生的 死鎖問題

            Thread 死鎖肯定是發(fā)生在2Thread 之間,AB B A,就產(chǎn)生了死鎖。大家看了上面測試之后一定會發(fā)現(xiàn),SendMessage 的死鎖和上面的第二個例子有關(guān)系,也就是 說 通過 Thread B W1 發(fā)送消息的時候又可能會產(chǎn)生死鎖。

             

             

            那么死鎖 何時產(chǎn)生呢 ?通過上面的例子我們知道了 如果Thread B W1 SendMessage一個消息,那么 Thread B 的這個SendMessage 就要等 Thread A 的隊列中的 消息執(zhí)行完畢才能夠返回,如果在 Thread B SendMessage 的同時 Thread A 等待 Thread B 中的某一處理完畢才能夠繼續(xù)處理消息的話,那么這個時候就發(fā)送了死鎖。

             

            我們繼續(xù)以測試來說明:

            測試3

                首先在 CMainFrame中加入一個 成員變量:m_bThreadExit Public

                我們將 UINT ThreadProc(LPVOID lParam) 加入一樣代碼如下:

                   UINT ThreadProc(LPVOID lParam)

            {

                      CMainFrame * v_pFrameWnd = (CMainFrame *)lParam;

                      if(v_pFrameWnd)

                      {

                         v_pFrameWnd->SendMessage(WM_USER + 100,0,0);

                      }

                      v_pFrameWnd->m_bThreadExit = TRUE;

                      return 0;

            }

            然后再 OnCreate 當中添加如下代碼:

                                         m_bThreadExit = FALSE;

                      AfxBeginThread(ThreadProc,this);

             

                      while(TRUE)

                      {

                         if(m_bThreadExit)

                            break;

                         Sleep(55);

            }

             

             OK 編譯 F5 運行 發(fā)現(xiàn)程序 進入無響應(yīng)狀態(tài),好這時我么讓程序 暫停:

            看看 2Thread Call Stack 都停在那里了?

            Main Thread如下:

             

            clip_image001此主題相關(guān)圖片如下:
            clip_image006

            在看看 另一個線成:



            clip_image001
            此主題相關(guān)圖片如下:
            clip_image007

            這會 是不是 很明了了

            MainThread 停在 循環(huán)內(nèi) 等待 m_bThreadExit True,而 另一個線成 則等待 MainThread 處理完畢 WM_USER + 100 這個消息,結(jié)果你等我,我等你,死了。。。。

            ()       處理辦法

            1 針對上面的例子 我們 可以通過 把SendMessage 改成 PostMessage 的方法來放棄等待。 這樣就解決了

            2 有些時候 第1種方法不符合要求比如下面這中情況

            UINT ThreadProc(LPVOID lParam)

            {

                      CMainFrame * v_pFrameWnd = (CMainFrame *)lParam;

                      if(v_pFrameWnd)

             

                      {

                      v_pFrameWnd->SetWindowText("lvyang");

                      }

                      v_pFrameWnd->m_bThreadExit = TRUE;

                      return 0;

            }

            這里面的CWnd::SetWindowText里面實際上調(diào)用的是::SetWindowText 而::SetWindowText 里面有調(diào)用 SendMessage 發(fā)送一個消息給CWnd 的窗口 ,因為::SetWindowText 內(nèi)部的我們沒有辦法來修改,那我只能去修改 MainThread 當中的 While 循環(huán)了。

             

            那如何修改呢? ThreadProc 當中 SetWindowText之所以被諸塞,就是因為 它向 MainThread SendMessage 的消息沒有得到處理,那么我們讓他處理的不就OK了嗎?好那我們就讓他處理,代碼如下:

            MSG msg;

                   while(TRUE)

                   {

                      if(m_bThreadExit)

                         break;

                      if(::PeekMessage(&msg,NULL,NULL,NULL,PM_NOREMOVE))

                      {

                         if(::GetMessage(&msg,NULL,NULL,NULL))

                         {

                            if(!PreTranslateMessage(&msg))

                            {

                               ::TranslateMessage(&msg);

                               ::DispatchMessage(&msg);

                            }

                         }

                      }

                      Sleep(55);

            }

            終于搞完了

            posted on 2008-12-19 08:42 Sandy 閱讀(1033) 評論(2)  編輯 收藏 引用 所屬分類: 雜項學習

            FeedBack:
            # re: 調(diào)用SendMessage 產(chǎn)生死鎖的問題分析
            2009-10-16 20:45 | ahenl
            SendMessage不放進隊列中的,msdn寫得很清楚:

            1. 同線程時,立即執(zhí)行窗口過程:If the specified window was created by the calling thread, the window procedure is called immediately as a subroutine.

            2. 不同線程時, 待接收線程從隊列中取消息時執(zhí)行窗口過程,并沒有說消息放進隊列中,只說明了處理時機: If the specified window was created by a different thread, the system switches to that thread and calls the appropriate window procedure. Messages sent between threads are processed only when the receiving thread executes message retrieval code.  回復(fù)  更多評論
              
            # re: 調(diào)用SendMessage 產(chǎn)生死鎖的問題分析
            91精品日韩人妻无码久久不卡 | 久久精品亚洲中文字幕无码麻豆| 很黄很污的网站久久mimi色| 久久亚洲AV无码西西人体| 无码任你躁久久久久久老妇| 色8久久人人97超碰香蕉987| 久久777国产线看观看精品| 久久免费99精品国产自在现线| 一级a性色生活片久久无少妇一级婬片免费放| 一级做a爰片久久毛片毛片| 久久精品亚洲精品国产色婷| 99久久综合国产精品二区| 久久亚洲AV无码精品色午夜| 国产成人久久激情91| 亚洲午夜无码久久久久小说| 国产99精品久久| 中文字幕亚洲综合久久菠萝蜜| 狠狠久久亚洲欧美专区| 久久频这里精品99香蕉久| 高清免费久久午夜精品| 色88久久久久高潮综合影院| 伊人久久大香线蕉无码麻豆| 99久久99久久精品国产| 久久久久无码精品国产| 久久综合久久美利坚合众国| 久久综合丝袜日本网| 久久精品www人人爽人人| 囯产极品美女高潮无套久久久| 久久综合精品国产一区二区三区| 国产高清美女一级a毛片久久w | 久久亚洲欧美国产精品| 一97日本道伊人久久综合影院| 亚洲国产成人久久精品动漫| 免费精品99久久国产综合精品| 无码人妻久久一区二区三区免费丨| 人妻少妇精品久久| 日韩精品久久久久久久电影| 午夜精品久久久内射近拍高清| 久久久久女教师免费一区| 国产精品激情综合久久| 久久久精品久久久久久|