• <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>
            春暖花開(kāi)
            雪化了,花開(kāi)了,春天來(lái)了
            posts - 149,comments - 125,trackbacks - 0

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

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

             

             

            ()       SendMessage 的工作機(jī)制

            首先我要先簡(jiǎn)要的說(shuō)明一個(gè)和這個(gè)話題有關(guān)系的消息處理機(jī)制:

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

             

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

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

             

            測(cè)試1我創(chuàng)建了一個(gè)無(wú)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 運(yùn)行程序 等到 程序停止在斷點(diǎn)上,我們看看Call Stack 的調(diào)用順序:


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

             

            然后 我又將 SendMessage 改成:

            PostMessage(WM_USER + 100,0,0);

            然后直接 F5 運(yùn)行程序 等到 程序停止在斷點(diǎn)上,我們?cè)倏纯?span>Call Stack 的調(diào)用順序:


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

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

            從這個(gè)測(cè)試?yán)拥慕Y(jié)果我判定SendMessage Thread A 中向 W1

            SendMessage 的消息根本不進(jìn)入消息隊(duì)列。

            測(cè)試2那么什么時(shí)候進(jìn)入隊(duì)列呢我來(lái)看看這個(gè)例子

            沿用上面那個(gè)例子的代碼我將 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 運(yùn)行 等待程序停在斷點(diǎn)處,看Call Stack 如下:


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

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

             

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

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

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

             

             

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

             

            我們繼續(xù)以測(cè)試來(lái)說(shuō)明:

            測(cè)試3

                首先在 CMainFrame中加入一個(gè) 成員變量: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 當(dāng)中添加如下代碼:

                                         m_bThreadExit = FALSE;

                      AfxBeginThread(ThreadProc,this);

             

                      while(TRUE)

                      {

                         if(m_bThreadExit)

                            break;

                         Sleep(55);

            }

             

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

            看看 2個(gè)Thread Call Stack 都停在那里了?

            Main Thread如下:

             

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

            在看看 另一個(gè)線成:



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

            這會(huì) 是不是 很明了了

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

            ()       處理辦法

            1 針對(duì)上面的例子 我們 可以通過(guò) 把SendMessage 改成 PostMessage 的方法來(lái)放棄等待。 這樣就解決了

            2 有些時(shí)候 第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里面實(shí)際上調(diào)用的是::SetWindowText 而::SetWindowText 里面有調(diào)用 SendMessage 發(fā)送一個(gè)消息給CWnd 的窗口 ,因?yàn)椋海?span>SetWindowText 內(nèi)部的我們沒(méi)有辦法來(lái)修改,那我只能去修改 MainThread 當(dāng)中的 While 循環(huán)了。

             

            那如何修改呢? ThreadProc 當(dāng)中 SetWindowText之所以被諸塞,就是因?yàn)?它向 MainThread SendMessage 的消息沒(méi)有得到處理,那么我們讓他處理的不就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 閱讀(1045) 評(píng)論(2)  編輯 收藏 引用 所屬分類(lèi): 雜項(xiàng)學(xué)習(xí)

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

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

            2. 不同線程時(shí), 待接收線程從隊(duì)列中取消息時(shí)執(zhí)行窗口過(guò)程,并沒(méi)有說(shuō)消息放進(jìn)隊(duì)列中,只說(shuō)明了處理時(shí)機(jī): 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ù)  更多評(píng)論
              
            # re: 調(diào)用SendMessage 產(chǎn)生死鎖的問(wèn)題分析
            久久久久久久亚洲Av无码| 免费精品久久天干天干| 亚洲国产成人久久精品影视| 亚洲嫩草影院久久精品| 久久久久亚洲精品天堂久久久久久 | 亚洲国产精品一区二区久久| 色综合久久88色综合天天| 亚洲欧美成人久久综合中文网| 无码AV波多野结衣久久| 久久九九久精品国产| 日韩av无码久久精品免费| 久久久久人妻精品一区三寸蜜桃| 久久精品国产久精国产果冻传媒| 日本久久久久久中文字幕| 人妻无码αv中文字幕久久琪琪布 人妻无码久久一区二区三区免费 人妻无码中文久久久久专区 | 无码国内精品久久综合88| 99久久免费国产精品| 久久青青草原精品国产| 精品国产乱码久久久久久人妻| 国产成人精品久久亚洲高清不卡 国产成人精品久久亚洲高清不卡 国产成人精品久久亚洲 | 久久精品一本到99热免费| 久久综合九色综合欧美就去吻| 国产精品久久久久久一区二区三区| 色综合久久天天综线观看| 日韩精品国产自在久久现线拍 | 欧美无乱码久久久免费午夜一区二区三区中文字幕 | 久久九九久精品国产免费直播| 91视频国产91久久久| 久久久精品国产sm调教网站| 99蜜桃臀久久久欧美精品网站| 久久久久国产一级毛片高清板| 久久精品嫩草影院| 国产精品久久波多野结衣| 久久天天躁狠狠躁夜夜躁2O2O| 国产精品成人久久久| 久久久久久久综合狠狠综合| 欧美久久一级内射wwwwww.| 亚洲日韩欧美一区久久久久我| 九九精品久久久久久噜噜| 久久人做人爽一区二区三区| 久久天天躁狠狠躁夜夜avapp |