以前一直在用线程,这两天又搞了一个线程用在一个小工具上,发现每次退出时总是卡住导致无响应只好强行关闭它。原以为是在目标电脑上有这个问题,结果今天在自己的电脑上也出现了,而且是必现,不过只在release下必现,而debug下没有出现过这个情况。
这个工具是个udp服务器,用来测试其它udp广播功能的。在启动之后,会创建一个线程,这个线程中不断地接收udp数据,如果接收到了就进行界面显示。大致的代码如下:
void DataRecvRoutine()
{
TCHAR szMsg[128];
_stprintf( szMsg, TEXT("udp server %d running~~~\n"), m_threadid );
OutputDebugString( szMsg );
int iret = 0;
int err = 0;
int iAddrLen = sizeof(sockaddr);
while ( m_bRunning )
{
err = 0;
iAddrLen = sizeof(sockaddr_in);
memset( m_pbuffer, 0, m_lBufferLen );
iret = recvfrom( m_socket, m_pbuffer, m_lBufferLen, 0, (sockaddr*)&gsckaddr_server, &iAddrLen );
if ( iret == SOCKET_ERROR )
{
err = WSAGetLastError();
break;
}
// 显示接收到的数据
CBRecData( m_pbuffer, iret );
}
// 恢复各状态变量
Sleep( 100 );
if ( m_socket != -1 )
{
closesocket( m_socket );
m_socket = -1;
}
_stprintf( szMsg, TEXT("udp server %d stopped ~~~\n"), m_threadid );
OutputDebugString( szMsg );
m_threadid = 0;
m_bRunning = false;
CloseHandle( m_hthread );
m_hthread = NULL; // 关闭线程句柄,使检测者可以进行检测
}
注意此线程函数,在退出时,会把它的线程句柄m_hthread关闭并置为NULL。在停止服务器的接口中,代码大致如下:
UINT32 StopServer()
{
if ( !m_bRunning )
{
return RME_OTHER_TASKNOTGO;
}
m_bRunning = false;
// 关闭soket,使接收函数返回-1从而退出while循环
if ( m_socket != -1 )
{
closesocket( m_socket );
m_socket = -1;
}
while ( m_hthread ); Sleep( 100 );
return RME_SUCCESS;
}
这个退出用m_hthread进行不断地检测,直到为NULL时退出然后返回。通过打印看,线程DataRecvRoutine()已经退出了,而且
_stprintf( szMsg, TEXT("udp server %d stopped ~~~\n"), m_threadid );
这一行的打印也出来了,后来在这个函数的最后一行加了打印,也有打印出来,这里看,就只有这个退出检测的while循环一直没有退出了。这个情况只在release下必现,在debug下从来没有出现过。我用的是vs2008编译。
后来为了解决这个问题,把这个退出检测改为正常的检测,使用事件进行检测,如下修改:
//while ( m_hthread ); Sleep( 100 );
::WaitForSingleObject( m_hthread, 10000 );
然后测试正常了。
在写这篇文章时,发现了在while(m_hthread)后面多写了个分号;,属于笔误,然后再进行修改:
while ( m_hthread ) Sleep( 100 );
//::WaitForSingleObject( m_hthread, 10000 );
再测试也正常了。
大致有如下的结论。release编译时会对空的循环进行优化操作,其导致了不该出现的问题。