CAsyncSocket对象不能跨线程之分析
2007-05-24 16:50
495 查看
现象
用多线程方法设计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中也许不会显现,但是你的程序肯定运行不正确,啥时候出错就不知道了。
相关文章推荐
- CAsyncSocket对象不能跨线程之分析 (转载)
- CAsyncSocket对象不能跨线程之分析
- CAsyncSocket对象不能跨线程之分析
- CAsyncSocket对象不能跨线程之分析
- CAsyncSocket对象不能跨线程之分析 (转载)
- CAsyncSocket对象不能跨线程之分析以及解决方案
- CAsyncSocket对象不能跨线程之分析
- CAsyncSocket对象不能跨线程之分析 (转载)
- CAsyncSocket对象不能跨线程之分析
- MFC中CAsyncSocket及其派生类对象跨线程使用方法
- MFC对象指针不能在线程间传输!
- 多线程委托之跨线程问题分析--在创建窗口句柄之前,不能在控件上调用 Invoke 或 BeginInvoke(解决方法已更新)
- MFC中创建多线程 MFC对象指针不能在线程间传输
- mfc对象指针不能在线程间传输!
- android 不能在子线程中更新ui的讨论和分析
- Windows中子线程不能触发定时器的问题分析
- Symbian中不能跨越线程(RThread)使用的对象/组件(RSocket/Memery Heap,etc)
- 从程序分析线程获得的是对象锁还是对象的方法锁?
- MFC中创建多线程 MFC对象指针不能在线程间传输
- lua 源码分析之线程对象lua_State