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

简介:
//线程参数
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,如需转载请自行联系原作者

目录
相关文章
|
8天前
|
安全 Java 应用服务中间件
Spring Boot + Java 21:内存减少 60%,启动速度提高 30% — 零代码
通过调整三个JVM和Spring Boot配置开关,无需重写代码即可显著优化Java应用性能:内存减少60%,启动速度提升30%。适用于所有在JVM上运行API的生产团队,低成本实现高效能。
77 3
|
11天前
|
存储 大数据 Unix
Python生成器 vs 迭代器:从内存到代码的深度解析
在Python中,处理大数据或无限序列时,迭代器与生成器可避免内存溢出。迭代器通过`__iter__`和`__next__`手动实现,控制灵活;生成器用`yield`自动实现,代码简洁、内存高效。生成器适合大文件读取、惰性计算等场景,是性能优化的关键工具。
133 2
|
8月前
|
消息中间件 Linux
Linux中的System V通信标准--共享内存、消息队列以及信号量
希望本文能帮助您更好地理解和应用System V IPC机制,构建高效的Linux应用程序。
277 48
|
8月前
|
监控 Java 计算机视觉
Python图像处理中的内存泄漏问题:原因、检测与解决方案
在Python图像处理中,内存泄漏是常见问题,尤其在处理大图像时。本文探讨了内存泄漏的原因(如大图像数据、循环引用、外部库使用等),并介绍了检测工具(如memory_profiler、objgraph、tracemalloc)和解决方法(如显式释放资源、避免循环引用、选择良好内存管理的库)。通过具体代码示例,帮助开发者有效应对内存泄漏挑战。
354 1
|
10月前
|
安全 Java 编译器
深入理解Java中synchronized三种使用方式:助您写出线程安全的代码
`synchronized` 是 Java 中的关键字,用于实现线程同步,确保多个线程互斥访问共享资源。它通过内置的监视器锁机制,防止多个线程同时执行被 `synchronized` 修饰的方法或代码块。`synchronized` 可以修饰非静态方法、静态方法和代码块,分别锁定实例对象、类对象或指定的对象。其底层原理基于 JVM 的指令和对象的监视器,JDK 1.6 后引入了偏向锁、轻量级锁等优化措施,提高了性能。
324 3
|
10月前
|
运维 监控 Java
为何内存不够用?微服务改造启动多个Spring Boot的陷阱与解决方案
本文记录并复盘了生产环境中Spring Boot应用内存占用过高的问题及解决过程。系统上线初期运行正常,但随着业务量上升,多个Spring Boot应用共占用了64G内存中的大部分,导致应用假死。通过jps和jmap工具排查发现,原因是运维人员未设置JVM参数,导致默认配置下每个应用占用近12G内存。最终通过调整JVM参数、优化堆内存大小等措施解决了问题。建议在生产环境中合理设置JVM参数,避免资源浪费和性能问题。
600 3
|
11月前
|
存储 架构师 Java
内存溢出原因与解决方案(4大主流方案详解)
本文详解内存溢出(OOM)的原因及解决方案。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
内存溢出原因与解决方案(4大主流方案详解)
|
11月前
|
设计模式 Java 开发者
Java多线程编程的陷阱与解决方案####
本文深入探讨了Java多线程编程中常见的问题及其解决策略。通过分析竞态条件、死锁、活锁等典型场景,并结合代码示例和实用技巧,帮助开发者有效避免这些陷阱,提升并发程序的稳定性和性能。 ####
|
10月前
|
存储 算法 Java
Java 内存管理与优化:掌控堆与栈,雕琢高效代码
Java内存管理与优化是提升程序性能的关键。掌握堆与栈的运作机制,学习如何有效管理内存资源,雕琢出更加高效的代码,是每个Java开发者必备的技能。
235 5
|
11月前
|
供应链 安全 NoSQL
PHP 互斥锁:如何确保代码的线程安全?
在多线程和高并发环境中,确保代码段互斥执行至关重要。本文介绍了 PHP 互斥锁库 `wise-locksmith`,它提供多种锁机制(如文件锁、分布式锁等),有效解决线程安全问题,特别适用于电商平台库存管理等场景。通过 Composer 安装后,开发者可以利用该库确保在高并发下数据的一致性和安全性。
150 6