浅谈一个线程通信代码的内存泄露及解决方案

None.gif // 线程参数
None.gif
typedef  struct  _MyData 
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif
public:
InBlock.gif    CString szFromPath;
//源目录
InBlock.gif
    CString szToPath;//目标目录
ExpandedBlockEnd.gif
}
 MYDATA,  * PMYDATA;
None.gif
None.gif
// 拷贝线程回送消息
None.gif
typedef  struct  _CopyResponse
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif    DWORD _copiedFiles;
InBlock.gif    CString msg;
//消息体
ExpandedBlockEnd.gif
}
COPYRESPONSE, * PCOPYRESPONSE;
None.gif
None.gif
None.gif
#define     WM_MYCOPYTHREAD (WM_USER+100)   // 来自拷贝线程的消息
None.gif
None.gif
// 保存创建的线程
None.gif
DWORD dwThreadId[MAX_THREADS];
None.gifHANDLE hThread[MAX_THREADS];
None.gif
None.gif
None.gif    hThread[
0 =  CreateThread(NULL, 0 , CopyFiles, & data, 0 , & dwThreadId[ 0 ]);     // 创建文件复制线程
None.gif

None.gifDWORD CopyFiles(LPVOID lParam)
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {//复制文件线程运行函数
InBlock.gif
    PMYDATA pData;
InBlock.gif    pData 
= (PMYDATA)lParam;
InBlock.gif    RealCopyFiles(pData
->szFromPath,pData->szToPath); //做实际的文件复制工作,这里面也有很多传给主线程的消息
InBlock.gif    
//构造传给主线程的消息体
InBlock.gif
    PCOPYRESPONSE pInfo = new COPYRESPONSE;
InBlock.gif    pInfo
->_copiedFiles = dwCopiedFilesNum;
InBlock.gif    pInfo
->msg = _T("END");
InBlock.gif    ::PostMessage(hMyMainWnd,WM_MYCOPYTHREAD,(WPARAM)pInfo,NULL);
//发出结束消息
InBlock.gif
    return 0;
ExpandedBlockEnd.gif}

None.gif
None.gifLRESULT  CMultiThreadBackDlg::OnCopyThreadMsg(WPARAM wParam,LPARAM lParam)
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {//处理来自拷贝线程的消息
InBlock.gif
    PCOPYRESPONSE pInfo = (PCOPYRESPONSE)wParam;
InBlock.gif
//…这里是具体的处理工作,比如更新界面上的进度条等等
InBlock.gif
    delete pInfo;
InBlock.gif    
return 0;
ExpandedBlockEnd.gif}

None.gif

这个方法思路比较清楚,启动子线程去负责复制文件,在子线程中不断地向主线程发出特定的消息,请求主线程去更新界面。分工合作,不错的实现,不是吗?遗憾的是,这个代码有一个致命的缺陷--内存泄露。你试着在子线程正在复制文件时,突然关闭对话框界面,看看你的VS中报告了什么,呵呵,眼熟的内存泄露来了。
2008061601.jpg

先别着急,google下看看高手们如何探讨这个问题的,个人推荐CSDN上的这个帖子http://topic.csdn.net/t/20040803/17/3239143.html,比较细致地分析了这种情况的原因和危害。问题的原因很简单,是因为在子线程里new了对象,post给父线程后希望由后者来delete掉这个对象,但当你强制关掉了对话框,此时父线程就挂掉了,那么自然没有人来帮你delete掉还没干掉的对象了,自然就泄露了。<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

      那么如何解决这个问题呢,帖子中的方法我没有去尝试,这里给出我的一个解决方案。

   我的解决方案主体代码:

None.gif // 线程参数
None.gif
typedef  struct  _MyData 
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif
public:
InBlock.gif    CString szFromPath;
//源目录
InBlock.gif
    CString szToPath;//目标目录
InBlock.gif
    CStatic* pCurFile;//显示当前复制的文件
ExpandedBlockEnd.gif
}
 MYDATA,  * PMYDATA;
None.gif
None.gif创建子线程代码:
None.gif        data.szFromPath 
=   this -> m_strFromPath; // 源目录
None.gif
        data.szToPath  =   this -> m_strFullToPath; // 目标目录
None.gif
        data.pCurFile  =   & m_static_info; // 把主界面的控件指针传给子线程
None.gif
        hThread[ 0 =  CreateThread(NULL, 0 , CopyFiles, & data, 0 , & dwThreadId[ 0 ]);     // 创建文件复制线程
None.gif

None.gifDWORD CMultiThreadBackDlg::CopyFiles(LPVOID lParam)
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {//复制文件线程运行函数
InBlock.gif
    PMYDATA pData;
InBlock.gif    pData 
= (PMYDATA)lParam;
InBlock.gif    
if(bChildExit==TRUE)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        
return 0;
ExpandedSubBlockEnd.gif    }

InBlock.gif    RealCopyFiles(pData
->szFromPath,pData->szToPath,pData);//做实际的文件复制工作
InBlock.gif
    pData->pCurFile->SetWindowTextW(_T("完成复制"));
InBlock.gif    
return 0;
ExpandedBlockEnd.gif}

None.gif
None.gif
void  CMultiThreadBackDlg::OnCancel()
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif    
this->ShowWindow(SW_HIDE);
InBlock.gif    bChildExit 
= TRUE;//设置子线程退出标志
InBlock.gif
    if(hThread[0]!=INVALID_HANDLE_VALUE)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{//等待子线程退出
InBlock.gif
        WaitForThreadToTerminate(hThread[0]);
InBlock.gif        hThread[
0= INVALID_HANDLE_VALUE;
ExpandedSubBlockEnd.gif    }

InBlock.gif    CDialog::OnCancel();
ExpandedBlockEnd.gif}

None.gif
None.gif
void  CMultiThreadBackDlg::PeekMsgLoop()
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {//自定义消息处理,防止主窗体阻塞消息处理
InBlock.gif
    MSG msg;
InBlock.gif    
while(::PeekMessage(&msg,this->GetSafeHwnd(),NULL,NULL,PM_REMOVE))
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        ::TranslateMessage(
&msg);
InBlock.gif        ::DispatchMessage(
&msg);
ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}

None.gif
None.gif
void  CMultiThreadBackDlg::WaitForThreadToTerminate(HANDLE  hThread)
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {//等待子线程终结
InBlock.gif
    DWORD dwRet;
InBlock.gif    
do
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        dwRet 
= ::MsgWaitForMultipleObjects(1,&hThread,FALSE,INFINITE,QS_ALLINPUT);
InBlock.gif        
if(dwRet!=WAIT_OBJECT_0)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            PeekMsgLoop();
ExpandedSubBlockEnd.gif        }

ExpandedSubBlockEnd.gif    }
while((dwRet!=WAIT_OBJECT_0)&&(dwRet!=WAIT_FAILED));
ExpandedBlockEnd.gif}

None.gif

      将界面主线程上控件的指针传入子线程,这避免了在堆中new对象然后post给主线程去delete这种不好的操作,但它本身并不能避免内存泄露,试想你突然强制关闭了对话框,而子线程还在运行中,那么子线程中引用它的指针就失效了,还是会出问题的。

      这里我采用的技巧是重载窗口关闭的处理函数,在这里提供自己的窗口消息处理函数去处理窗口的消息,防止窗口阻塞在这种无聊的事情上,同时设置子线程退出标志,并等待子线程完成,等这一切都处理完毕后我们才真正离开,为了避免产生时差误导用户,我们可以起先用this->ShowWindow(SW_HIDE);这样的代码来糊弄下用户。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值