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

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

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

    weidagang2046的專欄

    物格而后知致
    隨筆 - 8, 文章 - 409, 評論 - 101, 引用 - 0
    數據加載中……

    CAsyncSocket對象不能跨線程之分析

    現象

    用多線程方法設計socket程序時,你會發現在跨線程使用CAsyncSocket及其派生類時,會出現程序崩潰。所謂跨線程,是指該對象在一個線程中調用Create/AttachHandle/Attach函數,然后在另外一個線程中調用其他成員函數。下面的例子就是一個典型的導致崩潰的過程:
    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對象在主線程中被調用,在子線程中被關閉。

    跟蹤分析

    這個問題的原因可以通過單步跟蹤(F11)的方法來了解。我們在Socket.Create(0)處設斷點,跟蹤進去會發現下面的函數被調用:

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

    在這個函數的開頭,首先獲得了一個pState的指針指向_afxSockThreadState對象。從名字可以看出,這似乎是一個和線程相關的變量,實際上它是一個宏,定義如下:

    #define _afxSockThreadState AfxGetModuleThreadState()
    

    我們沒有必要去細究這個指針的定義是如何的,只要知道它是和當前線程密切關聯的,其他線程應該也有類似的指針,只是指向不同的結構。

    在這個函數中,CAsyncSocket創建了一個窗口,并把如下兩個信息加入到pState所管理的結構中:

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

    當調用Close時,我們再次跟蹤,就會發現在KillSocket中,下面的函數出現錯誤:

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

    我們在這個ASSERT處設置斷點,跟蹤進LookupHandle,會發現這個函數定義如下:

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

    顯然,這個函數試圖從當前線程查詢關于這個 socket的信息,可是這個信息放在創建這個socket的線程中,因此這種查詢顯然會失敗,最終返回NULL。

    有人會問,既然它是ASSERT出錯,是不是Release就沒問題了。這只是自欺欺人。ASSERT/VERIFY都是檢驗一些程序正常運行必須正確的條件。如果ASSERT都失敗,在Release中也許不會顯現,但是你的程序肯定運行不正確,啥時候出錯就不知道了。

    如何在多線程之間傳遞socket

    有些特殊情況下,可能需要在不同線程之間傳遞socket。當然我不建議在使用CAsyncSOcket的時候這么做,因為這增加了出錯的風險(尤其當出現拆解包問題時,有人稱為粘包,我基本不認同這種稱呼)。如果一定要這么做,方法應該是:

    1. 當前擁有這個socket的線程調用Detach方法,這樣socket句柄和C++對象及當前線程脫離關系
    2. 當前線程把這個對象傳遞給另外一個線程
    3. 另外一個線程創建新的CAsyncSocket對象,并調用Attach

    上面的例子,我稍微做修改,就不會出錯了:

    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) 評論(0)  編輯  收藏 所屬分類: Windows

    主站蜘蛛池模板: 亚洲AV无码专区亚洲AV伊甸园| 亚洲一区免费观看| 亚洲an日韩专区在线| 亚洲精品无码专区在线在线播放| 日韩激情无码免费毛片| 亚洲电影在线免费观看| 大地资源在线资源免费观看| 日韩在线观看免费完整版视频| 亚洲国产精品无码第一区二区三区 | 无码人妻精品中文字幕免费东京热| 一区二区三区免费电影| 精品韩国亚洲av无码不卡区| 中文字幕亚洲综合久久综合| 亚洲国产夜色在线观看| 亚洲黄色网址大全| 国产成人亚洲综合色影视| 精品亚洲一区二区三区在线观看 | 亚洲日韩aⅴ在线视频| 亚洲成A∨人片天堂网无码| 热99re久久精品精品免费| 久久久久久久91精品免费观看| 69免费视频大片| 1000部免费啪啪十八未年禁止观看| 久久一区二区三区免费播放| 久久福利青草精品资源站免费| 中国内地毛片免费高清| 中文字幕在线免费观看视频| caoporn成人免费公开| 国产免费伦精品一区二区三区 | 中文字幕日韩亚洲| 亚洲午夜激情视频| 国产亚洲精品看片在线观看| 国产精品xxxx国产喷水亚洲国产精品无码久久一区 | 日本亚洲免费无线码| 日韩欧毛片免费视频| 亚洲精品动漫免费二区| 日韩精品成人无码专区免费| 成在人线AV无码免费| 国产精品免费_区二区三区观看| 国产精品免费看香蕉| 国产成人99久久亚洲综合精品|