首页 新闻 论坛 群组 Blog 文档 下载 读书 Tag 网摘 搜索 .NET Java 游戏 视频 人才 外包 培训 数据库 书店 程序员
中国软件网
欢迎您:游客 | 登录 注册 帮助
  • 心跳包的线程同步问题 [已结帖,结帖人:paerxiushi]
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • paerxiushi
    • 等级:
    • 可用分等级:
    • 总技术分:
    • 总技术分排名:
    • 结帖率:
    发表于:2008-11-08 15:07:14 楼主
    本人的心跳包处理是这么设计的,在启动程序后,立刻开启一个心跳线程,专门用于处理客户的连接。这个线程用于处理所有的客户端的连接,当线程没有接到其中一个客户发来的请求达到20秒,即认为掉线。客户连接时,发送了一次数据之后,立刻退出。
    为了保证定时处理,启动了可等待定时器与事件机制。
    1.心跳线程是这么启动的:
    HANDLE hHeatBeat=CreateThread(NULL,0,CHeartBeat::WaitProc,NULL,0,NULL);
    CloseHandle(hHeatBeat);

    2.心跳包的主线程
    C/C++ code
    DWORD CALLBACK CHeartBeat::WaitProc(LPVOID lpVoid) { HANDLE hTimer; BOOL bSuccess; __int64 qwDueTime; LARGE_INTEGER liDueTime; char szError[255]; DWORD dwResult; HANDLE* phTimers=NULL,*phEvents=NULL; while(true) { if(hbList.size()>0) { //manage events phEvents=(HANDLE*)LocalAlloc(LPTR|LMEM_ZEROINIT,sizeof(HANDLE)*hbList.size()); for(vector<CHeartBeat*>::size_type i=0;i<hbList.size();++i) { if(hbList[i]->m_hEvent!=NULL) phEvents[i]=hbList[i]->m_hEvent; } //wait for one of event become signal, if there are no events become sigal,after 500 millisecond, it return dwResult=WaitForMultipleObjects((DWORD)hbList.size(),phEvents,FALSE,500); if(dwResult>=WAIT_OBJECT_0 && dwResult<=WAIT_OBJECT_0+hbList.size()-1) { vector<CHeartBeat*>::iterator iter=hbList.begin(); for (;iter!=hbList.end();++iter) { if((*iter)->m_hEvent==phEvents[dwResult-WAIT_OBJECT_0]) { if((*iter)->m_hTimer!=NULL) { //when follow MCT health messages was recevied CancelWaitableTimer((*iter)->m_hTimer); bSuccess = SetWaitableTimer( (*iter)->m_hTimer, // Handle to the timer object &liDueTime, // When timer will become signaled 1200000, // Periodic timer interval of 2 seconds NULL, NULL, FALSE ); // Do not restore a suspended system if (! bSuccess ) { sprintf( szError, "SetWaitableTimer failed with Error \ %d.\n", GetLastError() ); cout<<szError<<endl; CloseHandle( hTimer ); return false; } } else { if ( hTimer = CreateWaitableTimer( NULL, // Default security attributes FALSE, // Create auto-reset timer NULL ) ) // Name of waitable timer { // Create an integer that will be used to signal the timer // 120 seconds from now. qwDueTime = -20 * _SECOND; // Copy the relative time into a LARGE_INTEGER. liDueTime.LowPart = (DWORD) ( qwDueTime & 0xFFFFFFFF ); liDueTime.HighPart = (LONG) ( qwDueTime >> 32 ); bSuccess = SetWaitableTimer( hTimer, // Handle to the timer object &liDueTime, // When timer will become signaled 1200000, // Periodic timer interval of 120 seconds NULL, NULL, FALSE ); // Do not restore a suspended system if (! bSuccess ) { sprintf( szError, "SetWaitableTimer failed with Error \ %d.\n", GetLastError() ); cout<<szError<<endl; CloseHandle( hTimer ); return false; } (*iter)->m_hTimer=hTimer; } else { sprintf( szError, "CreateWaitableTimer failed with Error \ %d.\n", GetLastError() ); cout<<szError<<endl; return false; } } ResetEvent((*iter)->m_hEvent); } } } else if(dwResult==WAIT_FAILED) { OutputDebugString(_T("wait event singal failed\n")); } LocalFree((HLOCAL)phEvents); //manage waitable timers phTimers=(HANDLE*)LocalAlloc(LPTR|LMEM_ZEROINIT,sizeof(HANDLE)*(hbList.size())); for(vector<CHeartBeat*>::size_type i=0;i<hbList.size();++i) { if(hbList[i]->m_hTimer!=NULL) phTimers[i]=hbList[i]->m_hTimer; } //wait for one of event become signal, if there are no events become sigal,after 500 millisecond, it return DWORD dwResult=WaitForMultipleObjects((DWORD)(hbList.size()),phTimers,FALSE,500); if(dwResult>=WAIT_OBJECT_0 && dwResult<=WAIT_OBJECT_0+hbList.size()-1) { //a client is power off vector<CHeartBeat*>::iterator iter=hbList.begin(); int i=0; for (;iter!=hbList.end();++iter) { i++; if((*iter)->m_hTimer==phTimers[dwResult-WAIT_OBJECT_0]) { CHeartBeat* phb=(*iter); char mbProjNum[1024]; strcpy(mbProjNum,phb->m_strProj); printf("\na power off event occuered on ProjNum:%s\n",mbProjNum); //before remove element, clear event and timer SetEvent((*iter)->m_hEvent); WaitForSingleObject((*iter)->m_hEvent,INFINITE); CloseHandle((*iter)->m_hEvent); CloseHandle((*iter)->m_hTimer); //remove element hbList.erase(iter); break; } } } LocalFree((HLOCAL)phTimers); } //#region 1 //heart beat thread will exist if(WaitForSingleObject(m_hExitHandle,500)==WAIT_OBJECT_0) { if(hbList.size()>0) { vector<CHeartBeat*>::iterator iter=hbList.begin(); for(;iter!=hbList.end();++iter) { CHeartBeat* pHB=(CHeartBeat*)(*iter); if(pHB->m_hEvent!=NULL) { CloseHandle(pHB->m_hEvent); SetEvent(pHB->m_hEvent); WaitForSingleObject(pHB->m_hEvent,INFINITE); } if(pHB->m_hTimer!=NULL) { CancelWaitableTimer(pHB->m_hTimer); CloseHandle(pHB->m_hTimer); } SAFE_DELETE(pHB); } hbList.clear(); break; } } //#endregion } return true; }


    3.当有客户连接时,向心跳线程发送事件信号,对于客户每一次连接,服务端会创建一个心跳包对象,将其加入列表,而当客户后序连接时,服务端仅设置指定的事件信号
    C/C++ code
    bool bExist=false; CHeartBeat* pHeartBeat=NULL; for(vector<CHeartBeat*>::iterator iter=hbList.begin(); iter!=hbList.end(); ++iter) { if(strcmp((*iter)->GetProjNum(),pMessageInfo->msg_base_info.szPlanNumber)==0) { //IM has received health message previous for special plannumber bExist=true; pHeartBeat=*iter; break; } } if(bExist) { //notify CHeartBeat to reset waitable timer if(pHeartBeat!=NULL) SetEvent(pHeartBeat->GetEventHandle()); return true; } else { CHeartBeat* pHeart=new CHeartBeat(); //notify CHeartBeat to create waitable timer if(!pHeart->Start(pMessageInfo->msg_base_info.szPlanNumber)) { SAFE_DELETE(pHeart); return false; } hbList.push_back(pHeart); return true; } return true;


    3.以下是CHeartBeat类的Start方法:
    C/C++ code
    bool CHeartBeat::Start(const char* projNum) { HANDLE handle; handle=CreateEvent(NULL,TRUE,FALSE,NULL); if(handle==NULL) { return false; } m_hEvent=handle; SetEvent(m_hEvent); strcpy(m_strProj,projNum); return true; }


    4.当有服务端结束时,等待心跳线程退出
    HANDLE hExitHeartBeat=CreateEvent(NULL,TRUE,TRUE,NULL);
    CHeartBeat::SetExitEvent(hExitHeartBeat);
    WaitForSingleObject(hHeatBeat,INFINITE);,  (1)
    对于心跳线程的退出处理用代码段2中的region 1

    请问各位大侠,为什么当服务端运行到WaitForSingleObject(hHeatBeat,INFINITE)时,这个函数无法返回,而且心跳线程明明可以已经退出了。
    当把语句(1)修改成WaitForSingleObject(hHeatBeat,1000);时
    程序有时候会内存泄露? 
    60  修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • fogerasp
    • 等级:
    • 可用分等级:
    • 总技术分:
    • 总技术分排名:
    发表于:2008-11-08 16:45:151楼 得分:20
    mark

    C/C++学习交流
    欢迎热爱C/C++的人士加入群 72837978 一起来交流学习吧
    欢迎热爱C/C++的人士加入群 72837978 一起来交流学习吧
    欢迎热爱C/C++的人士加入群 72837978 一起来交流学习吧
    欢迎热爱C/C++的人士加入群 72837978 一起来交流学习吧
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • paerxiushi
    • 等级:
    • 可用分等级:
    • 总技术分:
    • 总技术分排名:
    发表于:2008-11-08 21:14:332楼 得分:0
    终于解决了,原来在结束线程的时候,要先检查一下线程是否退出,然后使用WaitForSingleObject等待线程变成有信号的.而且当要等待一个线程退出时,不能在创建线程之后就立刻关闭句柄,而是要在等待线程变成信号态之后再关闭句柄。
    因此
    代码段1与代码段3合并成:
    C/C++ code
    bool bExist=false; CHeartBeat* pHeartBeat=NULL; for(vector<CHeartBeat*>::iterator iter=hbList.begin(); iter!=hbList.end(); ++iter) { if(strcmp((*iter)->GetProjNum(),pMessageInfo->msg_base_info.szPlanNumber)==0) { //IM has received health message previous for special plannumber bExist=true; pHeartBeat=*iter; break; } } if(bExist) { //notify CHeartBeat to reset waitable timer if(pHeartBeat!=NULL) SetEvent(pHeartBeat->GetEventHandle()); return true; } else { if(hbList.size()==0) { g_hHeartBeat=CreateThread(NULL,0,CHeartBeat::WaitProc,NULL,0,NULL); } CHeartBeat* pHeart=new CHeartBeat(); //notify CHeartBeat to create waitable timer if(!pHeart->Start(pMessageInfo->msg_base_info.szPlanNumber)) { SAFE_DELETE(pHeart); return false; } hbList.push_back(pHeart); return true; }

    代码段4写成:
    C/C++ code
    DWORD dwExitCode; GetExitCodeThread(g_hHeartBeat,&dwExitCode); if(dwExitCode==STILL_ACTIVE) { HANDLE hExitHeartBeat=CreateEvent(NULL,TRUE,TRUE,NULL); CHeartBeat::SetExitEvent(hExitHeartBeat); WaitForSingleObject(g_hHeartBeat,INFINITE); CloseHandle(g_hHeartBeat); }

    只有当接收到消息时,才启动心跳线程,当心跳包列表为空时,立刻退出心跳线程。这样可以防止线程一直运行下去,因为在线程不调用WaitFor*处理而运行无限循环语句时,程序会吃CPU达99%。
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • paerxiushi
    • 等级:
    • 可用分等级:
    • 总技术分:
    • 总技术分排名:
    发表于:2008-11-08 21:15:593楼 得分:0
    这个贴子就当送分吧,进来的都有分。
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • xsc2001
    • 等级:
    • 可用分等级:
    • 总技术分:
    • 总技术分排名:
    发表于:2008-11-08 21:51:064楼 得分:40
    CloseHandle只能是将句柄关闭,不能将线程直接终止。
    另外对于线程的终止最好是让它自己主动终止,不要通过外界的管理程序去强制终止。如可能一个状态变量来控制,每次在循环执行之间先状态这个状态变量,通过状态变量决定是否要终止。外界只需要改变这个变量的值即可。
    while(m_Run)
    {
      ........;
      sleep(100);
    }
    //外界控制是只需要调用下面一行代码那可
    m_Run = false;
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • paerxiushi
    • 等级:
    • 可用分等级:
    • 总技术分:
    • 总技术分排名:
    发表于:2008-11-09 19:18:385楼 得分:0
    在线程函数中的那个Sleep是必须的,它可以防止线程吃CPU。那个状态变量最好不是全局的。
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • goga21cn
    • 等级:
    • 可用分等级:
    • 总技术分:
    • 总技术分排名:
    发表于:2008-12-01 12:49:116楼 得分:0
    mark!
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • hlovely
    • 等级:
    • 可用分等级:
    • 总技术分:
    • 总技术分排名:
    发表于:2008-12-01 12:58:117楼 得分:0
    学习
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • slimfeng
    • 等级:
    • 可用分等级:
    • 总技术分:
    • 总技术分排名:
    发表于:2008-12-01 13:15:048楼 得分:0
    mark
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • be_your_friend1
    • 等级:
    • 可用分等级:
    • 总技术分:
    • 总技术分排名:
    发表于:2008-12-01 13:22:489楼 得分:0
    学习下
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • hotblood003
    • 等级:
    • 可用分等级:
    • 总技术分:
    • 总技术分排名:
    发表于:2008-12-01 14:13:5910楼 得分:0
    学习
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • yljordan
    • 等级:
    • 可用分等级:
    • 总技术分:
    • 总技术分排名:
    发表于:2008-12-01 14:42:1811楼 得分:0
    想楼主学习。
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • mochencui
    • 等级:
    • 可用分等级:
    • 总技术分:
    • 总技术分排名:
    发表于:2008-12-01 15:19:3712楼 得分:0
    mark
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • superliyubo
    • 等级:
    • 可用分等级:
    • 总技术分:
    • 总技术分排名:
    发表于:2008-12-01 15:39:4613楼 得分:0
    mark~~~~~~~~~~~~~~~
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • chbrobin
    • 等级:
    • 可用分等级:
    • 总技术分:
    • 总技术分排名:
    发表于:2008-12-01 22:11:0414楼 得分:0
    不错哦
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • wheresky
    • 等级:
    • 可用分等级:
    • 总技术分:
    • 总技术分排名:
    发表于:2008-12-01 22:43:0515楼 得分:0
    学习一下,很好
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • ningyougang
    • 等级:
    • 可用分等级:
    • 总技术分:
    • 总技术分排名:
    发表于:2008-12-02 08:38:4016楼 得分:0
    对c/c++不是很熟悉,不过,还是给你顶起哦
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • simplebird
    • 等级:
    • 可用分等级:
    • 总技术分:
    • 总技术分排名:
    发表于:2008-12-02 08:44:5917楼 得分:0
    学习。
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • lja7912aaa
    • 等级:
    • 可用分等级:
    • 总技术分:
    • 总技术分排名:
    发表于:2008-12-02 09:53:4218楼 得分:0
    ding
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • ttppsoft
    • 等级:
    • 可用分等级:
    • 总技术分:
    • 总技术分排名:
    发表于:2008-12-02 15:04:2019楼 得分:0
    mark
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • taizans
    • 等级:
    • 可用分等级:
    • 总技术分:
    • 总技术分排名:
    发表于:2008-12-02 15:12:2320楼 得分:0
    学习~~~
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • tianwei118
    • 等级:
    • 可用分等级:
    • 总技术分:
    • 总技术分排名:
    发表于:2008-12-02 16:28:0221楼 得分:0
    結帖了,,也來mark下,,,,,