//
线程参数
typedef
struct
_MyData

{
public:
CStringszFromPath;//源目录
CStringszToPath;//目标目录
}
MYDATA,
*
PMYDATA;

//
拷贝线程回送消息
typedef
struct
_CopyResponse

{
DWORD_copiedFiles;
CStringmsg;//消息体
}
COPYRESPONSE,
*
PCOPYRESPONSE;


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

//
保存创建的线程
DWORDdwThreadId[MAX_THREADS];
HANDLEhThread[MAX_THREADS];


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

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

LRESULTCMultiThreadBackDlg::OnCopyThreadMsg(WPARAMwParam,LPARAMlParam)

{//处理来自拷贝线程的消息
PCOPYRESPONSEpInfo=(PCOPYRESPONSE)wParam;
//…这里是具体的处理工作,比如更新界面上的进度条等等
deletepInfo;
return0;
}
这个方法思路比较清楚,启动子线程去负责复制文件,在子线程中不断地向主线程发出特定的消息,请求主线程去更新界面。分工合作,不错的实现,不是吗?遗憾的是,这个代码有一个致命的缺陷--内存泄露。你试着在子线程正在复制文件时,突然关闭对话框界面,看看你的VS中报告了什么,呵呵,眼熟的内存泄露来了。

先别着急,google下看看高手们如何探讨这个问题的,个人推荐优快云上的这个帖子http://topic.youkuaiyun.com/t/20040803/17/3239143.html,比较细致地分析了这种情况的原因和危害。问题的原因很简单,是因为在子线程里new了对象,post给父线程后希望由后者来delete掉这个对象,但当你强制关掉了对话框,此时父线程就挂掉了,那么自然没有人来帮你delete掉还没干掉的对象了,自然就泄露了。<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
那么如何解决这个问题呢,帖子中的方法我没有去尝试,这里给出我的一个解决方案。
我的解决方案主体代码:
//
线程参数
typedef
struct
_MyData

{
public:
CStringszFromPath;//源目录
CStringszToPath;//目标目录
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
]);
//
创建文件复制线程
DWORDCMultiThreadBackDlg::CopyFiles(LPVOIDlParam)

{//复制文件线程运行函数
PMYDATApData;
pData=(PMYDATA)lParam;
if(bChildExit==TRUE)


{
return0;
}
RealCopyFiles(pData->szFromPath,pData->szToPath,pData);//做实际的文件复制工作
pData->pCurFile->SetWindowTextW(_T("完成复制"));
return0;
}

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()

{//自定义消息处理,防止主窗体阻塞消息处理
MSGmsg;
while(::PeekMessage(&msg,this->GetSafeHwnd(),NULL,NULL,PM_REMOVE))


{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
}

void
CMultiThreadBackDlg::WaitForThreadToTerminate(HANDLEhThread)

{//等待子线程终结
DWORDdwRet;
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);这样的代码来“糊弄”下用户。