<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    weidagang2046的專欄

    物格而后知致
    隨筆 - 8, 文章 - 409, 評(píng)論 - 101, 引用 - 0
    數(shù)據(jù)加載中……

    CAsyncSocket對(duì)象不能跨線程之分析

    現(xiàn)象

    用多線程方法設(shè)計(jì)socket程序時(shí),你會(huì)發(fā)現(xiàn)在跨線程使用CAsyncSocket及其派生類時(shí),會(huì)出現(xiàn)程序崩潰。所謂跨線程,是指該對(duì)象在一個(gè)線程中調(diào)用Create/AttachHandle/Attach函數(shù),然后在另外一個(gè)線程中調(diào)用其他成員函數(shù)。下面的例子就是一個(gè)典型的導(dǎo)致崩潰的過(guò)程:
    CAsyncSocket Socket;
    UINT Thread(LPVOID)
    {
           Socket.Close ();
           return 0;
    }
    void CTestSDlg::OnOK() 
    {
           // TODO: Add extra validation here
           Socket.Create(0);
           AfxBeginThread(Thread,0,0,0,0,0);
    }
    

    其中Socket對(duì)象在主線程中被調(diào)用,在子線程中被關(guān)閉。

    跟蹤分析

    這個(gè)問(wèn)題的原因可以通過(guò)單步跟蹤(F11)的方法來(lái)了解。我們?cè)赟ocket.Create(0)處設(shè)斷點(diǎn),跟蹤進(jìn)去會(huì)發(fā)現(xiàn)下面的函數(shù)被調(diào)用:

    void PASCAL CAsyncSocket::AttachHandle(
              SOCKET hSocket, CAsyncSocket* pSocket, BOOL bDead)
    {
        _AFX_SOCK_THREAD_STATE* pState = _afxSockThreadState;
        BOOL bEnable = AfxEnableMemoryTracking(FALSE);
        if (!bDead)
        {
                 ASSERT(CAsyncSocket::LookupHandle(hSocket, bDead) == NULL);
                 if (pState->m_pmapSocketHandle->IsEmpty())
                 {
                      ASSERT(pState->m_pmapDeadSockets->IsEmpty());
                      ASSERT(pState->m_hSocketWindow == NULL);
                      CSocketWnd* pWnd = new CSocketWnd;
                      pWnd->m_hWnd = NULL;
                      if (!pWnd->CreateEx(0, AfxRegisterWndClass(0),
                                       _T("Socket Notification Sink"),
                                     WS_OVERLAPPED, 0, 0, 0, 0, NULL, NULL))
                     {
                           TRACE0("Warning: unable to create socket notify window!\n");
                           AfxThrowResourceException();
                     }
                     ASSERT(pWnd->m_hWnd != NULL);
                     ASSERT(CWnd::FromHandlePermanent(pWnd->m_hWnd) == pWnd);
                     pState->m_hSocketWindow = pWnd->m_hWnd;
                }
                pState->m_pmapSocketHandle->SetAt((void*)hSocket, pSocket);
        }
        else
        {
               int nCount;
               if (pState->m_pmapDeadSockets->Lookup((void*)hSocket, (void*&)nCount))
                         nCount++;
               else
                         nCount = 1;
               pState->m_pmapDeadSockets->SetAt((void*)hSocket, (void*)nCount);
       }
       AfxEnableMemoryTracking(bEnable);
    }
    

    在這個(gè)函數(shù)的開(kāi)頭,首先獲得了一個(gè)pState的指針指向_afxSockThreadState對(duì)象。從名字可以看出,這似乎是一個(gè)和線程相關(guān)的變量,實(shí)際上它是一個(gè)宏,定義如下:

    #define _afxSockThreadState AfxGetModuleThreadState()
    

    我們沒(méi)有必要去細(xì)究這個(gè)指針的定義是如何的,只要知道它是和當(dāng)前線程密切關(guān)聯(lián)的,其他線程應(yīng)該也有類似的指針,只是指向不同的結(jié)構(gòu)。

    在這個(gè)函數(shù)中,CAsyncSocket創(chuàng)建了一個(gè)窗口,并把如下兩個(gè)信息加入到pState所管理的結(jié)構(gòu)中:

        pState->m_pmapSocketHandle->SetAt((void*)hSocket, pSocket);
        pState->m_pmapDeadSockets->SetAt((void*)hSocket, (void*)nCount);
        pState->m_hSocketWindow = pWnd->m_hWnd;
        pState->m_pmapSocketHandle->SetAt((void*)hSocket, pSocket);
    

    當(dāng)調(diào)用Close時(shí),我們?cè)俅胃櫍蜁?huì)發(fā)現(xiàn)在KillSocket中,下面的函數(shù)出現(xiàn)錯(cuò)誤:

        void PASCAL CAsyncSocket::KillSocket(SOCKET hSocket, CAsyncSocket* pSocket)
        {
                ASSERT(CAsyncSocket::LookupHandle(hSocket, FALSE) != NULL);
    

    我們?cè)谶@個(gè)ASSERT處設(shè)置斷點(diǎn),跟蹤進(jìn)LookupHandle,會(huì)發(fā)現(xiàn)這個(gè)函數(shù)定義如下:

    CAsyncSocket* PASCAL CAsyncSocket::LookupHandle(SOCKET hSocket, BOOL bDead)
    {
         CAsyncSocket* pSocket;
         _AFX_SOCK_THREAD_STATE* pState = _afxSockThreadState;
         if (!bDead)
         {
                 pSocket = (CAsyncSocket*)
                 pState->m_pmapSocketHandle->GetValueAt((void*)hSocket);
                 if (pSocket != NULL)
                      return pSocket;
        }
        else
        {
                 pSocket = (CAsyncSocket*)
                      pState->m_pmapDeadSockets->GetValueAt((void*)hSocket);
                 if (pSocket != NULL)
                       return pSocket;
        }
        return NULL;
    }
    

    顯然,這個(gè)函數(shù)試圖從當(dāng)前線程查詢關(guān)于這個(gè) socket的信息,可是這個(gè)信息放在創(chuàng)建這個(gè)socket的線程中,因此這種查詢顯然會(huì)失敗,最終返回NULL。

    有人會(huì)問(wèn),既然它是ASSERT出錯(cuò),是不是Release就沒(méi)問(wèn)題了。這只是自欺欺人。ASSERT/VERIFY都是檢驗(yàn)一些程序正常運(yùn)行必須正確的條件。如果ASSERT都失敗,在Release中也許不會(huì)顯現(xiàn),但是你的程序肯定運(yùn)行不正確,啥時(shí)候出錯(cuò)就不知道了。

    如何在多線程之間傳遞socket

    有些特殊情況下,可能需要在不同線程之間傳遞socket。當(dāng)然我不建議在使用CAsyncSOcket的時(shí)候這么做,因?yàn)檫@增加了出錯(cuò)的風(fēng)險(xiǎn)(尤其當(dāng)出現(xiàn)拆解包問(wèn)題時(shí),有人稱為粘包,我基本不認(rèn)同這種稱呼)。如果一定要這么做,方法應(yīng)該是:

    1. 當(dāng)前擁有這個(gè)socket的線程調(diào)用Detach方法,這樣socket句柄和C++對(duì)象及當(dāng)前線程脫離關(guān)系
    2. 當(dāng)前線程把這個(gè)對(duì)象傳遞給另外一個(gè)線程
    3. 另外一個(gè)線程創(chuàng)建新的CAsyncSocket對(duì)象,并調(diào)用Attach

    上面的例子,我稍微做修改,就不會(huì)出錯(cuò)了:

    CAsyncSocket Socket;
    UINT Thread(LPVOID sock)
    {
             Socket.Attach((SOCKET)sock);
             Socket.Close ();
             return 0;
    }
    void CTestSDlg::OnOK() 
    {
             // TODO: Add extra validation here
             Socket.Create(0);
             SOCKET hSocket = Socket.Detach ();
             AfxBeginThread(Thread,(LPVOID)hSocket,0,0,0,0);
    }
    


    from: http://blog.vckbase.com/arong/archive/2005/12/03/15578.html

    posted on 2006-11-07 20:07 weidagang2046 閱讀(483) 評(píng)論(0)  編輯  收藏 所屬分類: Windows

    主站蜘蛛池模板: 日本免费无遮挡吸乳视频电影| 精品在线视频免费| 在线观看特色大片免费网站| 久久精品国产亚洲av麻| 成在人线av无码免费高潮水| 亚洲伊人久久综合影院| 无码人妻丰满熟妇区免费| 亚洲砖码砖专无区2023| 69av免费视频| 亚洲人成综合在线播放| 大胆亚洲人体视频| 一级免费黄色大片| 亚洲成a人片在线观看中文!!!| 国产乱子伦精品免费无码专区| 国产成人亚洲精品无码AV大片| 国产成A人亚洲精V品无码| 免费毛片在线播放| 国产一区二区三区亚洲综合| 久久亚洲国产视频| 久久WWW免费人成一看片| 色老头综合免费视频| 亚洲级αV无码毛片久久精品| 无码一区二区三区免费视频| 成人无码a级毛片免费| 亚洲aⅴ无码专区在线观看春色| 亚洲第一成年男人的天堂| 日韩精品电影一区亚洲| 男女超爽刺激视频免费播放| 精精国产www视频在线观看免费| 亚洲精品国产字幕久久不卡 | 18禁网站免费无遮挡无码中文 | 中国人xxxxx69免费视频| 亚洲AV综合色区无码二区偷拍| 久久久久亚洲精品天堂久久久久久 | 亚洲人成毛片线播放| 亚洲人成亚洲人成在线观看| 久久不见久久见免费视频7| 国产乱妇高清无乱码免费| 蜜桃传媒一区二区亚洲AV| 亚洲欧洲国产精品久久| 亚洲AV人无码综合在线观看|