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

简介:
//线程参数
typedef struct _MyData 
{
public:
    CString szFromPath;//源目录
    CString szToPath;//目标目录
} MYDATA, *PMYDATA;

//拷贝线程回送消息
typedef struct _CopyResponse
{
    DWORD _copiedFiles;
    CString msg;//消息体
}COPYRESPONSE,*PCOPYRESPONSE;


#define    WM_MYCOPYTHREAD (WM_USER+100)  //来自拷贝线程的消息

//保存创建的线程
DWORD dwThreadId[MAX_THREADS];
HANDLE hThread[MAX_THREADS];


    hThread[0] = CreateThread(NULL,0, CopyFiles,&data,0,&dwThreadId[0]);    //创建文件复制线程

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

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


这个方法思路比较清楚,启动子线程去负责复制文件,在子线程中不断地向主线程发出特定的消息,请求主线程去更新界面。分工合作,不错的实现,不是吗?遗憾的是,这个代码有一个致命的缺陷--内存泄露。你试着在子线程正在复制文件时,突然关闭对话框界面,看看你的VS中报告了什么,呵呵,眼熟的内存泄露来了。
2008061601.jpg
先别着急,google下看看高手们如何探讨这个问题的,个人推荐CSDN上的这个帖子http://topic.csdn.net/t/20040803/17/3239143.html,比较细致地分析了这种情况的原因和危害。问题的原因很简单,是因为在子线程里new了对象,post给父线程后希望由后者来delete掉这个对象,但当你强制关掉了对话框,此时父线程就挂掉了,那么自然没有人来帮你delete掉还没干掉的对象了,自然就泄露了。
      那么如何解决这个问题呢,帖子中的方法我没有去尝试,这里给出我的一个解决方案。
   我的解决方案主体代码:
//线程参数
typedef struct _MyData 
{
public:
    CString szFromPath;//源目录
    CString szToPath;//目标目录
    CStatic* pCurFile;//显示当前复制的文件
} MYDATA, *PMYDATA;

创建子线程代码:
        data.szFromPath = this->m_strFromPath;//源目录
        data.szToPath = this->m_strFullToPath;//目标目录
        data.pCurFile = &m_static_info;//把主界面的控件指针传给子线程
        hThread[0] = CreateThread(NULL,0, CopyFiles,&data,0,&dwThreadId[0]);    //创建文件复制线程

DWORD CMultiThreadBackDlg::CopyFiles(LPVOID lParam)
{//复制文件线程运行函数
    PMYDATA pData;
    pData = (PMYDATA)lParam;
    if(bChildExit==TRUE)
    {
        return 0;
    }
    RealCopyFiles(pData->szFromPath,pData->szToPath,pData);//做实际的文件复制工作
    pData->pCurFile->SetWindowTextW(_T("完成复制"));
    return 0;
}

void CMultiThreadBackDlg::OnCancel()
{
    this->ShowWindow(SW_HIDE);
    bChildExit = TRUE;//设置子线程退出标志
    if(hThread[0]!=INVALID_HANDLE_VALUE)
    {//等待子线程退出
        WaitForThreadToTerminate(hThread[0]);
        hThread[0] = INVALID_HANDLE_VALUE;
    }
    CDialog::OnCancel();
}

void CMultiThreadBackDlg::PeekMsgLoop()
{//自定义消息处理,防止主窗体阻塞消息处理
    MSG msg;
    while(::PeekMessage(&msg,this->GetSafeHwnd(),NULL,NULL,PM_REMOVE))
    {
        ::TranslateMessage(&msg);
        ::DispatchMessage(&msg);
    }
}

void CMultiThreadBackDlg::WaitForThreadToTerminate(HANDLE  hThread)
{//等待子线程终结
    DWORD dwRet;
    do
    {
        dwRet = ::MsgWaitForMultipleObjects(1,&hThread,FALSE,INFINITE,QS_ALLINPUT);
        if(dwRet!=WAIT_OBJECT_0)
        {
            PeekMsgLoop();
        }
    }while((dwRet!=WAIT_OBJECT_0)&&(dwRet!=WAIT_FAILED));
}


      将界面主线程上控件的指针传入子线程,这避免了在堆中new对象然后post给主线程去delete这种不好的操作,但它本身并不能避免内存泄露,试想你突然强制关闭了对话框,而子线程还在运行中,那么子线程中引用它的指针就失效了,还是会出问题的。
      这里我采用的技巧是重载窗口关闭的处理函数,在这里提供自己的窗口消息处理函数去处理窗口的消息,防止窗口阻塞在这种无聊的事情上,同时设置子线程退出标志,并等待子线程完成,等这一切都处理完毕后我们才真正离开,为了避免产生“时差”误导用户,我们可以起先用this->ShowWindow(SW_HIDE);这样的代码来“糊弄”下用户。



本文转自Phinecos(洞庭散人)博客园博客,原文链接:http://www.cnblogs.com/phinecos/archive/2008/06/16/1223326.html,如需转载请自行联系原作者

目录
相关文章
|
1月前
|
存储 SQL 缓存
揭秘Java并发核心:深度剖析Java内存模型(JMM)与Volatile关键字的魔法底层,让你的多线程应用无懈可击
【8月更文挑战第4天】Java内存模型(JMM)是Java并发的核心,定义了多线程环境中变量的访问规则,确保原子性、可见性和有序性。JMM区分了主内存与工作内存,以提高性能但可能引入可见性问题。Volatile关键字确保变量的可见性和有序性,其作用于读写操作中插入内存屏障,避免缓存一致性问题。例如,在DCL单例模式中使用Volatile确保实例化过程的可见性。Volatile依赖内存屏障和缓存一致性协议,但不保证原子性,需与其他同步机制配合使用以构建安全的并发程序。
60 0
|
2天前
|
存储 缓存 Java
java线程内存模型底层实现原理
java线程内存模型底层实现原理
java线程内存模型底层实现原理
|
13天前
|
Arthas 监控 Java
监控线程池的内存使用情况以预防内存泄漏
监控线程池的内存使用情况以预防内存泄漏
|
18天前
|
监控 Java 数据库连接
使用线程池时,如何避免内存泄漏的问题?
使用线程池时,如何避免内存泄漏的问题?
|
13天前
|
监控 数据可视化 Java
使用JDK自带的监控工具JConsole来监控线程池的内存使用情况
使用JDK自带的监控工具JConsole来监控线程池的内存使用情况
|
29天前
|
缓存 Java
Java内存管理秘籍:掌握强软弱幻四大引用,让代码效率翻倍!
【8月更文挑战第29天】在Java中,引用是连接对象与内存的桥梁,主要分为强引用、软引用、弱引用和幻象引用。强引用确保对象生命周期由引用控制,适用于普通对象;软引用在内存不足时可被回收,适合用于内存敏感的缓存;弱引用在无强引用时即可被回收,适用于弱关联如监听器列表;幻象引用需与引用队列配合使用,用于跟踪对象回收状态,适用于执行清理工作。合理使用不同类型的引用车可以提升程序性能和资源管理效率。
37 4
|
29天前
|
存储 缓存 JSON
一行代码,我优化掉了1G内存占用
这里一行代码,指的是:String.intern()的调用,为了调用这一行代码,也写了几十行额外的代码。
|
1月前
|
Java Windows
【Azure Developer】Windows中通过pslist命令查看到Java进程和线程信息,但为什么和代码中打印出来的进程号不一致呢?
【Azure Developer】Windows中通过pslist命令查看到Java进程和线程信息,但为什么和代码中打印出来的进程号不一致呢?
|
1月前
|
前端开发 JavaScript Java
揭开 JavaScript 垃圾回收的秘密——一场与内存泄漏的生死较量,让你的代码从此焕然一新!
【8月更文挑战第23天】本文通过多个实例深入探讨了JavaScript中的垃圾回收机制及其对应用性能的影响。首先介绍了基本的内存管理方式,随后分析了变量不再使用时的回收过程。接着,通过事件监听器未被移除及全局变量管理不当等场景展示了常见的内存泄漏问题。最后,文章介绍了使用`WeakRef`和`FinalizationRegistry`等现代API来有效避免内存泄漏的方法。理解并运用这些技术能显著提升Web应用的稳定性和效率。
76 0
|
1月前
|
Web App开发 缓存 JavaScript
技术分享:深入探索内存泄漏——识别、分类与解决方案
【8月更文挑战第27天】在软件开发的浩瀚星海中,内存管理始终是程序员们必须面对的重要课题。内存泄漏,作为内存管理不善的典型症状,不仅影响应用性能,还可能导致系统崩溃,是每位开发者都需警惕的“暗礁”。本文将带您深入探索内存泄漏的本质、常见类型及有效的解决策略,助力您的工作学习之旅更加顺畅。
42 0

相关实验场景

更多