MFC中CAsyncSocket及其派生類對象跨線程使用方法
存在的現象
在MFC中用多線程方法開發WinSocket通訊程序時,如果你的的是API方式,自然沒有以下說的問題。但如果當你使用CAsyncSocket及其派生類(CSocket或是你自己的寫的)來開發的話,會發現在不同線程中使用CAsyncSocket及其派生類對象時,會出現程序崩潰。這裏所說的跨線程,是指該對象在一個線程中調用Create/Close/Attach/Detach函數,然後在另外一個線程中調用其他成員函數如Receive/ReceiveFrom/Send/SendTo等。下面是一個典型的導致崩潰的例子程序(片斷):
CAsyncSocket async_socket;
UINT ThreadProc(LPVOID) //出錯:1
{
TRACE("======== ThreadDeattch出錯:1 ========/n");
async_socket.Close (); //錯誤發生在這個調用上:要進行關閉async_socket對象.
return 0;
}
void CTsAsncSocketDlg::OnBnClickedButtonCreate() //出錯:1
{
async_socket.Create(0);//async_socket對象在主線程中被建立
::AfxBeginThread(ThreadProc,0,0,0,0,0); //async_socket對象在主線程中被傳遞到另外的線程:ThreadProc
}
要想知道錯誤的原因,可以跟進去分析。這樣說起來又太繁瑣了,網上有不少類似的文章,問題的關鍵是不要在不同線程中進行Create/Close調用。
首先,當你的程序有問題的時候,怎樣來判斷是因爲這個問題?這是關鍵一步。調試/斷點/觀測等有很多手段,VC++的IDE在個這個方面是非常出色的。這裏還有個簡單的方法。比如有如下代碼:
class CSocketP3PServer : public CAsyncSocket
{
public:
CSocketP3PServer ();
~ CSocketP3PServer ();
public:
.......... //others
#if _DEBUG
private:
DWORD m_sockCurrentThreadId;
#endif //_DEUBG
};
void CSocketP3PServer::CSocketP3PServer()
{
#if _DEBUG
m_sockCurrentThreadId = ::GetCurrentThreadId();
#endif //_DEBUG
}
CSocketP3PServer::~CSocketP3PServer()
{
#if _DEBUG
if(m_sockCurrentThreadId != ::GetCurrentThreadId())
{
//如果程序走到這裏來了,那麼應該是跨線程使用造成的問題
TRACE("======= CSocketP3PServer 對象(Address:%d)在不同線程調用了 ========/n",this);
}
#endif //_DEBUG
}
知道了問題所在,解決的辦法就很好說了,網上也有很多相關的介紹文章。這裏是把這些解決方法整理一下。
解決的思路主要有2條:
1:避免這個問題的出現,也就是說避免在跨線程中使用CAsyncSocket及其派生類對象。
關於這點有很多的招數,比如說可以 A線程專門作收發工作,而其它線程把相關的數據通過數據共享或是其它手段在彼此之間傳遞。這個就是一個<讀者-作者>之間的關係。還有其它的辦法,總之,就是要把socket對象整個生命期都在單一的線程上。
2:實在是沒有辦法避免不在跨線程中使用CAsyncSocket及其派生類對象的,可以通過在線程之間傳遞SOCKET <變量>來實現,這裏給個例子,代碼如下:
UINT ThreadDeattchMe(LPVOID pv) //正確的在跨線程使用as_socket,這裏是工作線程A
{
TRACE("======== ThreadDeattch 正確的在跨線程使用SOCKET ========/n");
as_socket.Attach((SOCKET)pv); //<---------- 注意這裏傳遞的是 SOCKET <變量>
as_socket.Close ();
return 0;
}
void CTsAsncSocketDlg::OnBnClickedButtonCreate()//正確的在跨線程使用SOCKET,這裏是主線程
{
as_socket.Create(0);
AfxBeginThread(ThreadDeattchMe,(LPVOID)as_socket.Detach(),0,0,0,0);//<---------- 注意這裏傳遞的是 SOCKET <變量>
}
21:10 2007-4-4