700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > MFC多线程的创建 包括工作线程和用户界面线程

MFC多线程的创建 包括工作线程和用户界面线程

时间:2019-05-04 18:22:03

相关推荐

MFC多线程的创建 包括工作线程和用户界面线程

MFC多线程的创建

1.MFC多线程简介

MFC对多线程进行了一层简单的封装,在Visual C++中每个线程都是从CWinThread类继承而来的。每一个应用程序的执行都有一个主线程,这个主线程也是从CWinThread类继承而来的。可以利用CWinThread对象创建应用程序执行的其它线程。

MFC用CWinThread对象来表示所有线程。利用MFC可以创建两种线程,分别称之为工作者线程和用户界面线程。二者的主要区别在于工作者线程没有消息循环,而用户界面线程有自己的消息队列和消息循环。工作者线程没有消息机制,通常用来执行后台计算和维护任务,如冗长的计算过程,打印机的后台打印等。用户界面线程一般用于处理独立于其他线程执行之外的用户输入,响应用户及系统所产生的事件和消息等。但对于Win32的API编程而言,这两种线程是没有区别的,它们都只需线程的启动地址即可启动线程来执行任务。

2.MFC多线程基础

为了深入了解MFC下创建线程的方法,我们先深入学习一下CWinThread类。CWinThread类在MFC类结构中的位置如下图所示:

图1:CWinThread类在mfc类结构中的位置

详细的MFC类结构图请参考MSDN:/zh-cn/library/ws8s10w4(VS.90).aspx

首先看一下类CWinThread的声明。类CWinThread的声明在afxwin.h中:

/

ClassCWinThread:publicCCmdTarget

{

DECLARE_DYNAMIC(CWinThread)

public:

//构造函数

CWinThread();

//用来具体创建线程的函数

BOOLCreateThread(DWORDdwCreateFlags= 0,UINTnStackSize= 0,

LPSECURITY_ATTRIBUTESlpSecurityAttrs=NULL);

// Attributes

//主窗口,(通常使用AfxGetApp()->m_pMainWnd可以得到)

CWnd*m_pMainWnd;// main window(usually same AfxGetApp()->m_pMainWnd)

//活动窗口,可能不是主窗口

CWnd*m_pActiveWnd;// active mainwindow (may not be m_pMainWnd)

BOOLm_bAutoDelete;// enables'delete this' after thread termination

// only valid while running

//该线程的句柄

HANDLEm_hThread;// thisthread's HANDLE

operatorHANDLE()const;

//该线程的ID

DWORDm_nThreadID;// thisthread's ID

//线程优先级

intGetThreadPriority();

BOOLSetThreadPriority(intnPriority);

// Operations

//挂起线程

DWORDSuspendThread();

//启动线程

DWORDResumeThread();

//发送线程消息

BOOLPostThreadMessage(UINTmessage,WPARAMwParam,LPARAMlParam);

// Overridables

//线程初始化,每个应用程序都可以重载该函数

virtualBOOLInitInstance();

// running and idle processing

virtualintRun();

virtualBOOLPreTranslateMessage(MSG*pMsg);

virtualBOOLPumpMessage();// low level message pump

virtualBOOLOnIdle(LONGlCount);// return TRUEif more idle processing

virtualBOOLIsIdleMessage(MSG*pMsg);// checks for special messages

// thread termination

virtualintExitInstance();//default will 'delete this'

// Advanced: exception handling

virtualLRESULTProcessWndProcException(CException*e,constMSG*pMsg);

// Advanced: handling messages sent to message filter hook

virtualBOOLProcessMessageFilter(intcode,LPMSGlpMsg);

// Advanced: virtual access to m_pMainWnd

virtualCWnd*GetMainWnd();

// Implementation

public:

virtual~CWinThread();

#ifdef_DEBUG

virtualvoidAssertValid()const;

virtualvoidDump(CDumpContext&dc)const;

#endif

voidCommonConstruct();

virtualvoidDelete();

// 'delete this' only if m_bAutoDelete == TRUE

public:

// constructor used by implementation of AfxBeginThread

CWinThread(AFX_THREADPROCpfnThreadProc,LPVOIDpParam);

// valid after construction

LPVOIDm_pThreadParams;// generic parameters passed to starting function

AFX_THREADPROCm_pfnThreadProc;

// set after OLE is initialized

void(AFXAPI*m_lpfnOleTermOrFreeLib)(BOOL,BOOL);

COleMessageFilter*m_pMessageFilter;

protected:

BOOLDispatchThreadMessageEx(MSG*msg);// helper

voidDispatchThreadMessage(MSG*msg);// obsolete

};

/

有些函数是不是很眼熟呀,在前面的文章中已经介绍和使用过啦。MFC类就是这样的,它无非就是简单封装一些API函数,并添加一些自己的函数而构成的。不用MFC我们照样可以编写很优秀的程序,MFC的宗旨就是简化程序设计,让你可以很容易入门和简单的使用,也催生了大量的程序员。但对喜欢刨根问底的朋友却是一道很厚的墙

下面几个函数是多线程编程中经常用到的几个全局函数:

//创建工作线程

CWinThread*AFXAPIAfxBeginThread(

AFX_THREADPROCpfnThreadProc,//线程函数

LPVOIDpParam,//传给线程函数的参数

intnPriority=THREAD_PRIORITY_NORMAL,//线程的优先级

UINTnStackSize= 0,//堆栈大小

DWORDdwCreateFlags= 0,//创建起始状态标志

LPSECURITY_ATTRIBUTESlpSecurityAttrs=NULL//线程的安全属性

);

//创建用户界面线程

CWinThread*AFXAPIAfxBeginThread(

CRuntimeClass*pThreadClass,//从CWinThread派生的类的RUNTIME_CLASS

intnPriority=THREAD_PRIORITY_NORMAL,//线程的优先级

UINTnStackSize= 0,// 堆栈大小

DWORDdwCreateFlags= 0,// 创建起始状态标志

LPSECURITY_ATTRIBUTESlpSecurityAttrs=NULL//线程的安全属性

);

//获取线程对象

CWinThread*AFXAPIAfxGetThread();

//获取当前消息

MSG*AFXAPIAfxGetCurrentMessage();

//结束线程执行

voidAFXAPIAfxEndThread(UINTnExitCode,BOOLbDelete=TRUE);

//初始化线程

voidAFXAPIAfxInitThread();

//终止线程执行

voidAFXAPIAfxTermThread(HINSTANCEhInstTerm=NULL);

仔细阅读以上类的说明能学到不少东西:

(1) CWinThead类通过CreateThread()成员函数来创建线程,这个函数的声明和Win32APICreateThread()的参数相似

(2)每个函数在运行后都有一个句柄和ID号。

(3)通过设置属性m_bAutoDelete,可决定线程在运行结束后线程对象是否自动删除,它的访问权限是public型的,可以直接进行设置。一般情况下,线程对象的生命周期和线程的生命周期一致。如果你想改变线程对象的生命周期,可设置该属性为FALSE。

(4)MFC下的多线程仍然支持线程的挂起和启动。

(5)具有PreTranslateMessage()、PumpMessage()等函数,供用户界面线程的消息机制使用。

在MFC中实际上是调用AfxBeginThread()函数来创建线程的。那么为什么不直接使用::CreateThread()或_beginthread()函数来创建线程呢?只要看一下CWinThread类的实现中的相关代码就明白了。在thrdcore.cpp文件中的相关代码如下:

==========================================================================================================

CWinThread*AFXAPIAfxGetThread()

{

// check for current thread in module thread state

AFX_MODULE_THREAD_STATE*pState=AfxGetModuleThreadState();

CWinThread*pThread=pState->m_pCurrentWinThread;

returnpThread;

}

MSG*AFXAPIAfxGetCurrentMessage()

{

_AFX_THREAD_STATE*pState=AfxGetThreadState();

ASSERT(pState);

return&(pState->m_msgCur);

}

CWinThread* AFXAPI AfxBeginThread(AFX_THREADPROC pfnThreadProc, LPVOID pParam,int nPriority,UINT nStackSize, DWORD dwCreateFlags,

LPSECURITY_ATTRIBUTESlpSecurityAttrs)

{

#ifndef_MT

pfnThreadProc;

pParam;

nPriority;

nStackSize;

dwCreateFlags;

lpSecurityAttrs;

returnNULL;

#else

ASSERT(pfnThreadProc!=NULL);

CWinThread* pThread=DEBUG_NEWCWinThread(pfnThreadProc,pParam);

ASSERT_VALID(pThread);

if(!pThread->CreateThread(dwCreateFlags|CREATE_SUSPENDED,nStackSize,

lpSecurityAttrs))

{

pThread->Delete();

returnNULL;

}

VERIFY(pThread->SetThreadPriority(nPriority));

if(!(dwCreateFlags&CREATE_SUSPENDED))

VERIFY(pThread->ResumeThread() != (DWORD)-1);

returnpThread;

#endif//!_MT)

}

CWinThread* AFXAPI AfxBeginThread(CRuntimeClass*pThreadClass,

intnPriority,UINTnStackSize,DWORDdwCreateFlags,

LPSECURITY_ATTRIBUTESlpSecurityAttrs)

{

#ifndef_MT

pThreadClass;

nPriority;

nStackSize;

dwCreateFlags;

lpSecurityAttrs;

returnNULL;

#else

ASSERT(pThreadClass!=NULL);

ASSERT(pThreadClass->IsDerivedFrom(RUNTIME_CLASS(CWinThread)));

CWinThread* pThread= (CWinThread*)pThreadClass->CreateObject();

if(pThread==NULL)

AfxThrowMemoryException();

ASSERT_VALID(pThread);

pThread->m_pThreadParams=NULL;

if(!pThread->CreateThread(dwCreateFlags|CREATE_SUSPENDED,nStackSize,

lpSecurityAttrs))

{

pThread->Delete();

returnNULL;

}

VERIFY(pThread->SetThreadPriority(nPriority));

if(!(dwCreateFlags&CREATE_SUSPENDED))

{

ENSURE(pThread->ResumeThread() != (DWORD)-1);

}

returnpThread;

#endif//!_MT

}

voidAFXAPIAfxEndThread(UINTnExitCode,BOOLbDelete)

{

#ifndef_MT

nExitCode;

bDelete;

#else

// remove current CWinThread object from memory

AFX_MODULE_THREAD_STATE*pState=AfxGetModuleThreadState();

CWinThread*pThread=pState->m_pCurrentWinThread;

if(pThread!=NULL)

{

ASSERT_VALID(pThread);

ASSERT(pThread!=AfxGetApp());

// cleanup OLE if required

if(pThread->m_lpfnOleTermOrFreeLib!=NULL)

(*pThread->m_lpfnOleTermOrFreeLib)(TRUE,FALSE);

if(bDelete)

pThread->Delete();

pState->m_pCurrentWinThread=NULL;

}

// allow cleanup of any thread local objects

AfxTermThread();

// allow C-runtime to cleanup, and exit the thread

_endthreadex(nExitCode);

#endif//!_MT

}

/

// Global functions forthread initialization and thread cleanup

LRESULTCALLBACK_AfxMsgFilterHook(intcode,WPARAMwParam,LPARAMlParam);

voidAFXAPIAfxInitThread()

{

if(!afxContextIsDLL)

{

// set message filter proc

_AFX_THREAD_STATE*pThreadState=AfxGetThreadState();

ASSERT(pThreadState->m_hHookOldMsgFilter==NULL);

pThreadState->m_hHookOldMsgFilter= ::SetWindowsHookEx(WH_MSGFILTER,

_AfxMsgFilterHook,NULL,::GetCurrentThreadId());

}

}

externCThreadSlotData*_afxThreadData;

voidAFXAPIAfxTermThread(HINSTANCEhInstTerm)

{

try

{

#ifdef_DEBUG

// check for missing AfxLockTempMap calls

if(AfxGetModuleThreadState()->m_nTempMapLock!= 0)

{

TRACE(traceAppMsg,0,"Warning: Temp map lock count non-zero(%ld).\n",

AfxGetModuleThreadState()->m_nTempMapLock);

}

#endif

AfxLockTempMaps();

AfxUnlockTempMaps(-1);

}

catch(CException*e)

{

e->Delete();

}

try

{

// cleanup thread local tooltip window

if(hInstTerm==NULL)

{

AFX_MODULE_THREAD_STATE*pModuleThreadState=AfxGetModuleThreadState();

if((pModuleThreadState!=NULL) &&

(pModuleThreadState->m_pToolTip!=NULL))

{

pModuleThreadState->m_pToolTip->DestroyWindow();

deletepModuleThreadState->m_pToolTip;

pModuleThreadState->m_pToolTip=NULL;

}

}

}

catch(CException*e)

{

e->Delete();

}

try

{

// cleanup the rest of the thread local data

if(_afxThreadData!=NULL)

_afxThreadData->DeleteValues(hInstTerm,FALSE);

}

catch(CException*e)

{

e->Delete();

}

}

/

// CWinThread construction

CWinThread::CWinThread(AFX_THREADPROCpfnThreadProc,LPVOIDpParam)

{

m_pfnThreadProc=pfnThreadProc;

m_pThreadParams=pParam;

CommonConstruct();

}

CWinThread::CWinThread()

{

m_pThreadParams=NULL;

m_pfnThreadProc=NULL;

CommonConstruct();

}

voidCWinThread::CommonConstruct()

{

m_pMainWnd=NULL;

m_pActiveWnd=NULL;

// no HTHREAD until it is created

m_hThread=NULL;

m_nThreadID= 0;

_AFX_THREAD_STATE*pState=AfxGetThreadState();

// initialize message pump

#ifdef_DEBUG

pState->m_nDisablePumpCount= 0;

#endif

pState->m_msgCur.message=WM_NULL;

pState->m_nMsgLast=WM_NULL;

::GetCursorPos(&(pState->m_ptCursorLast));

// most threads are deleted when not needed

m_bAutoDelete=TRUE;

// initialize OLE state

m_pMessageFilter=NULL;

m_lpfnOleTermOrFreeLib=NULL;

}

CWinThread::~CWinThread()

{

// free thread object

if(m_hThread!=NULL)

CloseHandle(m_hThread);

// cleanup module state

AFX_MODULE_THREAD_STATE*pState=AfxGetModuleThreadState();

if(pState->m_pCurrentWinThread==this)

pState->m_pCurrentWinThread=NULL;

}

BOOL CWinThread::CreateThread(DWORDdwCreateFlags,UINTnStackSize,

LPSECURITY_ATTRIBUTESlpSecurityAttrs)

{

#ifndef_MT

dwCreateFlags;

nStackSize;

lpSecurityAttrs;

returnFALSE;

#else

ENSURE(m_hThread==NULL);// already created?

// setup startup structure for thread initialization

_AFX_THREAD_STARTUPstartup;memset(&startup,0,sizeof(startup));

startup.pThreadState=AfxGetThreadState();

startup.pThread=this;

startup.hEvent= ::CreateEvent(NULL,TRUE,FALSE,NULL);

startup.hEvent2= ::CreateEvent(NULL,TRUE,FALSE,NULL);

startup.dwCreateFlags=dwCreateFlags;

if(startup.hEvent==NULL||startup.hEvent2==NULL)

{

TRACE(traceAppMsg,0,"Warning: CreateEvent failed inCWinThread::CreateThread.\n");

if(startup.hEvent!=NULL)

::CloseHandle(startup.hEvent);

if(startup.hEvent2!=NULL)

::CloseHandle(startup.hEvent2);

returnFALSE;

}

// create the thread (it may or may not start to run)

m_hThread= (HANDLE)(ULONG_PTR)_beginthreadex(lpSecurityAttrs,nStackSize,

&_AfxThreadEntry, &startup,dwCreateFlags|CREATE_SUSPENDED,(UINT*)&m_nThreadID);

if(m_hThread==NULL)

{

::CloseHandle(startup.hEvent);

::CloseHandle(startup.hEvent2);

returnFALSE;

}

// start the thread just for MFC initialization

VERIFY(ResumeThread()!= (DWORD)-1);

VERIFY(::WaitForSingleObject(startup.hEvent,INFINITE) ==WAIT_OBJECT_0);

::CloseHandle(startup.hEvent);

// if created suspended, suspend it until resume threadwakes it up

if(dwCreateFlags&CREATE_SUSPENDED)

VERIFY(::SuspendThread(m_hThread) != (DWORD)-1);

// if error during startup, shut things down

if(startup.bError)

{

VERIFY(::WaitForSingleObject(m_hThread,INFINITE)==WAIT_OBJECT_0);

::CloseHandle(m_hThread);

m_hThread=NULL;

::CloseHandle(startup.hEvent2);

returnFALSE;

}

// allow thread to continue, once resumed (it may alreadybe resumed)

VERIFY(::SetEvent(startup.hEvent2));

returnTRUE;

#endif//!_MT

}

voidCWinThread::Delete()

{

// delete thread if it is auto-deleting

if(m_bAutoDelete)

deletethis;

}

/

在创建一个新的线程时,不必直接创建线程对象,因为线程对象是由全局函数AxCreateThread()自动产生的。只要首先定义一个CWinThread类指针,然后调用全局函数AxCreateThread()来产生一个新的线程对象,并调用线程类的CreateThread()成员函数来具体创建线程,最后将新的线程对象的指针返回。应该将其存在CWinThread变量中,以便能够进一步控制线程。

AxCreateThread()函数和CWinThread:: CreateThread()函数并不是简单地对_beginthreadex()函数进行了一下封装,它还做了一些应用程序框架所需的内部数据的初始化工作,并保证使用正确的C运行库版本,因为在两个函数一开始就要检查环境参数_MT是否已经定义。而且在创建线程过程中还进行了很多检查和测试,保证能够正确产生新的线程,同时在线程创建失败时正确地释放掉已经分配的资源。

注意事项:

(1)一般情况下,推荐使用AfxBeginThread()来一次性地创建并启动一个线程,但是也可以通过两步法来创建线程:首先创建CWinThread类的一个对象,然后调用该对象的成员函数CreateThread()来启动该线程。

(2)MFC支持两类多线程,即工作线程和用户界面线程。用户界面线程经常重载InitInstance()和ExitInstance()函数,用以控制用户界面线程实例的初始化和必要的清理工作。工作者线程一般不使用

3.线程函数

(1)工作线程的线程函数:从AfxBeginThread()函数的参数可以看出:

AFX_THREADPROCpfnThreadProc,//线程函数

LPVOIDpParam, //传给线程函数的参数

其中AFX_THREADPROC为一个宏,其定义如下:

typedefUINT(AFX_CDECL*AFX_THREADPROC)(LPVOID);

从以上语句,可以得到工作线程的线程函数的形式为:

UINT ThreadFunc(LPVOID pParm);

ThreadFunc()函数应返回一个UINT类型的值,用以指明该函数结束的原因。一般情况下,返回0表明执行成功。

pParam:传递给线程函数的一个32位参数,执行函数将用某种方式解释该值。它可以是数值,或是指向一个结构的指针,甚至可以被忽略。

(2)用户界面线程的线程函数:从AfxBeginThread()函数的参数可以看出:

CRuntimeClass*pThreadClass,//从CWinThread派生的类的RUNTIME_CLASS

即pThreadClass 是指向 CWinThread 的一个导出类的运行时类对象的指针,该导出类定义了被创建的用户界面线程的启动、退出等。

有了上面的基础,下面来学习线程的创建。

4.工作线程的创建

工作线程没有消息机制,经常用来完成一些后台工作,如计算、打印、等待、循环等,这样用户就不必因为计算机在从事繁杂而耗时的工作而等待。创建一个工作线程相对比较简单。

我们用一个实例来演示工作线程的创建。查找N(很大)以内的素数,为了演示效果,N的取值很大(比如:10000等),并在主界面实时动态显示处理结果,并显示处理进度等。

先说下素数的定义:素数又称质数,指在一个大于1的自然数中,除了1和此整数自身外,不能被其他自然数整除的数。

一个数n如果是素数,那么它的所有的因子不超过sqrt(n)(即n的平方),那么我们可以用这个性质用判断一个数是否是素数。在此不讨论该算法的复杂度,只是演示效果,望大牛们不要拍砖哈。

主要程序:

//.h文件

UINTThreadFunc(LPVOIDpParm);//线程函数的定义

boolIsPrime(UINTNumber);//素数判断

structthreadInfo

{

unsignedintnRange;//范围

HWNDhWnd;//主窗口句柄,用于消息的发送

};

private:

CWinThread*m_pThread;

//.cpp文件

//开始

voidCAfxBeginThread1Dlg::OnBnClickedButtonCal()

{

// TODO:在此添加控件通知处理程序代码

m_nNum= 1;

m_List.ResetContent();

UpdateData(TRUE);

m_stTip.SetWindowText("");

if(m_nRange<2)

{

AfxMessageBox("查找范围必须是大于的整数!",MB_OK|MB_ICONERROR);

return;

}

GetDlgItem(IDC_BUTTON_CAL)->EnableWindow(FALSE);

m_Progress.SetPos(0);

m_Progress.SetRange32(0,m_nRange);

m_Info.nRange=m_nRange;

m_Info.hWnd=m_hWnd;

m_pThread=AfxBeginThread(ThreadFunc,&m_Info);

if(m_pThread==NULL)

{

AfxMessageBox("启动失败!",MB_OK|MB_ICONERROR);

return;

}

}

UINTThreadFunc(LPVOIDpParm)

{

threadInfo*pInfo=(threadInfo*)pParm;

boolbResult=false;

for(inti=2;i<=pInfo->nRange;i++)

{

bResult=IsPrime(i);

::SendMessage(pInfo->hWnd,WM_INFO,bResult,i);

//Sleep(1000);

}

//结束

::SendMessage(pInfo->hWnd,WM_INFO,0,-1);

return0;

}

//素数判断

boolIsPrime(UINTnNumber)

{//定理:如果n不是素数, 则n有满足<d<=sqrt(n)的一个因子d.

if(nNumber< 2)returnfalse;

if(nNumber== 2)returntrue;

for(inti= 3;i*i<=nNumber;i+= 2)

{

if(nNumber%i== 0)

returnfalse;

}

returntrue;

}

//显示消息处理函数

LRESULTCAfxBeginThread1Dlg::OnMyInfo(WPARAMwParam,LPARAMlParam)

{

if(wParam== 0&&lParam== -1)

{//结束

m_stTip.SetWindowText("完成");

GetDlgItem(IDC_BUTTON_CAL)->EnableWindow(TRUE);

}

else

{//是素数

m_Progress.SetPos(lParam);

CStringstr;

str.Format("%d",lParam);

m_stTip.SetWindowText(str);

if(wParam)

{

str.Format("%d个",m_nNum);

GetDlgItem(IDC_STATIC1)->SetWindowText(str);

str.Format("第%d个:%d",m_nNum++,lParam);

m_List.AddString(str);

}

}

return0;

}

结果预览:

工程源码下载地址:

/detail/cbnotes/4923353

欢迎大家修改和指正。

注意事项:

(1)该方式的多线程和前面的_beginthreadex方式很想象,一般用于后台的工作执行。

(2)注意信息的实时显示。

(3)创建线程时多参数的传递,此处采用结构体的方式。

5.用户界面线程的创建

由于用户界面线程含有自己的消息循环,可以处理Windows消息,并可创建和管理诸如窗口和控件等用户界面元素。因此,这种线程较工作线程更为复杂。

创建用户界面线程的起点是从MFC的CWinThread类派生一个定制的线程类,而不是调用AfxBeginThead()函数。定制的线程类必须重载InitInstance()函数,该函数用来执行初始化任务,在创建线程时系统将调用InitInstance()函数。最好还要重载ExitInstane()函数,该函数是InitInstance()函数的对应,MFC在删除线程对象之前会调用ExitInstane()函数,以便线程能够在结束后清除自身。

用户界面线程的创建有两种方法,方法一是首先从CWinThread类派生一个类(必须要用宏DECLARE_DYNCREATE和IMPLEMENT_DYNCREATE对该类进行声明和实现),然后调用AfxBeginThead()创建CWinThread派生类的对象进行初始化,启动线程运行。方法二是先通过构造函数创建类CWinThread的一个对象,然后由程序员调用函数::CreateThread来启动线程。通常CWinThread类的对象在该线程的生存期结束时将自动终止,如果程序员希望自己来控制,则需要将m_bAutoDelete设为FALSE。这样在线程终止之后,CWinThread类对象仍然存在,此时需要手动删除CWinThread对象。

5.1用户界面线程实例1

该实例主要演示创建用户界面线程的第一种方法,先从CWinThread类派生一个类,然后调用AfxBeginThead()创建CWinThread派生类的对象进行初始化,启动线程运行。该实例主要演示文件的移动,并实施显示操作的进度。设计到两个线程:文件的读写采用工作线程,进度显示采用用户界面线程。

主要程序:

//.h文件

defineSTEPLEN1024*64//文件一次读写的进步k

UINT ThreadFunc(LPVOID pParm);//线程函数的定义:文件复制线程

CWinThread*pUIThread;//界面线程,用于进度的显示

CWinThread*pThread;//工作线程,用于文件的复制

CStringm_szSrcPath;//源文件路径

CStringm_szDesPath;//目标文件路径

//.CPP文件

//文件复制模块:

voidCAfxBeginThead2Dlg::OnBnClickedButtonCopy()

{

// TODO:在此添加控件通知处理程序代码

if(m_szDesPath.IsEmpty() ||m_szSrcPath.IsEmpty())

{

AfxMessageBox("源文件路径和目标文件路径不能为空!",MB_OK|MB_ICONERROR);

return;

}

//

GetDlgItem(IDC_BUTTON_COPY)->EnableWindow(FALSE);

//创建用户界面线程,用于进度的显示

pUIThread=AfxBeginThread(RUNTIME_CLASS(CcbCopyFile));

if(pUIThread==NULL)

{

AfxMessageBox("用户界面线程启动失败!",MB_OK|MB_ICONERROR);

GetDlgItem(IDC_BUTTON_COPY)->EnableWindow(TRUE);

return;

}

//传递参数

pUIThread->PostThreadMessage(WM_THREADINFO,0,(LPARAM)m_szSrcPath.GetBuffer(0));

pUIThread->PostThreadMessage(WM_THREADINFO,1,(LPARAM)m_szDesPath.GetBuffer(0));

//创建工作线程,用于文件的复制

pThread=AfxBeginThread(ThreadFunc,this);

if(pThread==NULL)

{

AfxMessageBox("工作线程启动失败!",MB_OK|MB_ICONERROR);

GetDlgItem(IDC_BUTTON_COPY)->EnableWindow(TRUE);

return;

}

SetTimer(1,1000,NULL);//速度统计

SetTimer(2,100,NULL);//操作计时

m_stTip.SetWindowText("进行中...");

pUIThread->PostThreadMessage(WM_THREADINFO,2,1);//启动

}

//文件复制线程

UINTThreadFunc(LPVOIDpParm)

{

CAfxBeginThead2Dlg*pInfo=(CAfxBeginThead2Dlg*)pParm;

//打开源文件,得到文件的大小,并一块一块的读取

CFileReadFile;

BOOLbOpen=ReadFile.Open(pInfo->m_szSrcPath,CFile::modeRead);

if(!bOpen)

{

AfxMessageBox(pInfo->m_szSrcPath+_T(" :文件打开失败!"),MB_ICONERROR|MB_OK);

pInfo->m_stTip.SetWindowText("复制【失败】!");

pInfo->GetDlgItem(IDC_BUTTON_COPY)->EnableWindow(TRUE);

return1;

}

//得到文件的大小,用于计算进度

DWORDdwTotalSize=ReadFile.GetLength();

DWORDdwCompleteSize= 0;//已完成的大小

//计算文件去读的步长

DWORDdwStep=STEPLEN>dwTotalSize?dwTotalSize:STEPLEN;

//数据缓冲区

char*pBuf=newchar[dwStep+1];

memset(pBuf,0x00,dwStep+1);

DWORDdwRead=dwStep;

//创建目标文件,若目标文件存在则清空

CFileWriteFile;

bOpen=WriteFile.Open(pInfo->m_szDesPath,CFile::modeCreate|CFile::modeWrite);

if(!bOpen)

{

AfxMessageBox(pInfo->m_szDesPath+_T(" :文件打开失败!"),MB_ICONERROR|MB_OK);

pInfo->m_stTip.SetWindowText("复制【失败】!");

pInfo->GetDlgItem(IDC_BUTTON_COPY)->EnableWindow(TRUE);

return2;

}

//文件的复制:从源文件去读取数据,并写入到目标文件中

while( (dwRead=ReadFile.Read(pBuf,dwStep))> 0 )

{//读取源文件,一次一块

//将读取的数据写入目标文件中

WriteFile.Write(pBuf,dwRead);

dwCompleteSize+=dwRead;

pInfo->m_nSpeed+=dwRead;

//更新进度

pInfo->pUIThread->PostThreadMessage(WM_THREADINFO,3, (LPARAM)int((dwCompleteSize*1.0/dwTotalSize)*100));

}

//完成

deletepBuf;

//关闭文件

ReadFile.Close();

WriteFile.Close();

//发送结束消息,用于关闭进度显示模块

pInfo->pUIThread->PostThreadMessage(WM_THREADINFO,10, 1);

pInfo->KillTimer(1);

pInfo->KillTimer(2);

pInfo->m_stTip.SetWindowText("复制完成!");

//使能复制按钮,以便可以继续进行

pInfo->GetDlgItem(IDC_BUTTON_COPY)->EnableWindow(TRUE);

return0;

}

//用户界面线程类:派生于CWinThread类

//文件复制模块:头文件

classCcbCopyFile:publicCWinThread

{

DECLARE_DYNCREATE(CcbCopyFile)

protected:

CcbCopyFile();//动态创建所使用的受保护的构造函数

virtual~CcbCopyFile();

CStringm_szSrcPath,m_szDesPath;

public:

CCopyFileDlg*m_pProgressDlg;//进度界面

virtualBOOLInitInstance();

virtualintExitInstance();

afx_msgvoidOnThreadInfo(WPARAMwParam,LPARAMlParam);

protected:

DECLARE_MESSAGE_MAP()

};

===================================================================

// cbCopyFile.cpp :实现文件

//

#include"stdafx.h"

#include"AfxBeginThead2.h"

#include"cbCopyFile.h"

// CcbCopyFile

IMPLEMENT_DYNCREATE(CcbCopyFile,CWinThread)

CcbCopyFile::CcbCopyFile()

{

m_pProgressDlg=NULL;

}

CcbCopyFile::~CcbCopyFile()

{

}

BOOLCcbCopyFile::InitInstance()

{

// TODO:在此执行任意逐线程初始化

returnTRUE;

}

intCcbCopyFile::ExitInstance()

{

// TODO:在此执行任意逐线程清理

returnCWinThread::ExitInstance();

}

BEGIN_MESSAGE_MAP(CcbCopyFile,CWinThread)

ON_THREAD_MESSAGE(WM_THREADINFO,&CcbCopyFile::OnThreadInfo)

END_MESSAGE_MAP()

// CcbCopyFile消息处理程序

//显示消息处理函数

voidCcbCopyFile::OnThreadInfo(WPARAMwParam,LPARAMlParam)

{

if(wParam== 0)

{//源文件路径参数

m_szSrcPath.Format("%s",lParam);

//AfxMessageBox(m_szSrcPath);

}

elseif(wParam== 1)

{//目标文件路径参数

m_szDesPath.Format("%s",lParam);

//AfxMessageBox(m_szDesPath);

}

elseif(wParam== 2)

{//启动

m_pProgressDlg=newCCopyFileDlg;

m_pProgressDlg->Create(IDD_DIALOG1);

m_pProgressDlg->m_szSrcPath=m_szSrcPath;

m_pProgressDlg->m_szDesPath=m_szDesPath;

m_pProgressDlg->UpdateData(FALSE);

m_pProgressDlg->ShowWindow(TRUE);

}

elseif(wParam== 3)

{//进度

m_pProgressDlg->m_Progress.SetPos(lParam);

}

elseif(wParam== 4)

{//速度

m_pProgressDlg->UpdateSpeed(lParam);

}

elseif(wParam== 5)

{//时间

float*p= (float*)lParam;

m_pProgressDlg->UpdateTime(*p);

}

else

{//完成

m_pProgressDlg->OnCancel();

}

//return 0;

}

结果预览:

工程源码下载地址:

/detail/cbnotes/4956549

欢迎大家修改和指正。

注意事项:

(1)该实例源码比较多,所以上面只贴出了重要的部分,建议下载整个工程源码下来学习和研究(地址见上)。

(2)该实例主要演示了文件复制,采用了工作线程和用户界面线程相配合。提供了实时的进度显示、实时复制速度显示和时间显示。功能还是比较实用,只要稍加修改做成一个复制模块(dll)用于自己的程序中,还是非常实用。对不喜欢采用CopyFile/CopyFileEx函数的朋友是一个不错的选择。

(3)注意在工作线程中,响应消息的方式,消息映射和平时的不一样,主要采用ON_THREAD_MESSAGE,而不是ON _MESSAGE。

(4)注意工作线程创建中,传递给线程函数的参数为this,即pThread=AfxBeginThread(ThreadFunc,this);//即传递的是对象的地址。这对于要传递很多参数的是一个不错的选择。

消息响应函数的格式为:void OnThreadInfo(WPARAMwParam,LPARAM lParam);

注意返回类型必须为void

(5)该实例还有一个不完善的地方,就是复制过程中按“取消”的处理,该实例中还没有实现,望有心的朋友可以实现它,大家多多思考和动手,对你一定有很好的帮助的。

(6)用户界面线程和程序的主线程很相像,能处理各种消息。

5.2用户界面线程实例2

该实例主要演示创建用户界面线程的第二种方法,先从CWinThread类派生一个类,然后调用CreateThread创建CWinThread派生类的对象进行初始化,启动线程运行。该实例主要演示手气测试,设计到两个用户界面线程:数字输入线程和结果判断线程。

主要程序:

//主线程

CInputThread*m_pInputThread;//数字输入线程

CResultThread *m_pResultThread;//结果判断线程

//测试

voidCAfxBeginThead3Dlg::OnBnClickedButtonTest()

{

// TODO:在此添加控件通知处理程序代码

m_pInputThread=newCInputThread;

m_pInputThread->CreateThread(CREATE_SUSPENDED);

m_pResultThread=newCResultThread;

m_pResultThread->CreateThread(CREATE_SUSPENDED);

m_pResultThread->m_hWnd=m_hWnd;

m_pInputThread->m_pResultThread=m_pResultThread;

m_pInputThread->ResumeThread();//运行

m_pResultThread->ResumeThread();

m_nTimes++;

// UpdateInfo();

}

LRESULTCAfxBeginThead3Dlg::OnMessage(WPARAMwParam,LPARAMlParam)

{

if(lParam== 1)

{//成功一次

m_nSucess++;

}

UpdateInfo();

if(wParam== 1)

{//再测一次

OnBnClickedButtonTest();

}

return0;

}

//更新信息

voidCAfxBeginThead3Dlg::UpdateInfo(void)

{

CStringstr;

str.Format("统计:%d/%d",m_nSucess,m_nTimes);

GetDlgItem(IDC_STATIC1)->SetWindowText(str);

}

//数字输入线程

CResultThread *m_pResultThread;//结果判断线程

CInputDlgm_InputDlg;//输入对话框

BOOLCInputThread::InitInstance()

{

// TODO:在此执行任意逐线程初始化

m_InputDlg.DoModal();

returnTRUE;

}

intCInputThread::Run()

{

// TODO:在此添加专用代码和/或调用基类

m_pResultThread->PostThreadMessage(WM_THREADMESSAGE,0,m_InputDlg.m_nInput);

returnCWinThread::Run();

}

//结果判断线程

#defineWM_THREADMESSAGEWM_USER+100//输入数字线程发送的消息,用于结果判断

#defineWM_MESSAGEWM_USER+200//向主线程发送消息,用于返回相关信息

BEGIN_MESSAGE_MAP(CResultThread,CWinThread)

ON_THREAD_MESSAGE(WM_THREADMESSAGE,&CResultThread::OnThreadMessage)

END_MESSAGE_MAP()

//显示消息处理函数

voidCResultThread::OnThreadMessage(WPARAMwParam,LPARAMlParam)

{

if(wParam== 0)

{

boolbSucess=false;

CStringstr;

intnLuckNum=GetLuckNum();

if(lParam==nLuckNum)

{

bSucess=true;

str.Format("恭喜,手气不错哦!\n=============\n你测试的数字为【%d】\n当前的幸运数字为【%d】\n再测一次吧?",lParam,nLuckNum);

}

else

{

bSucess=false;

str.Format("很遗憾,差一点点哦!\n=============\n你测试的数字为【%d】\n当前的幸运数字为【%d】\n再测一次吧?",lParam,nLuckNum);

}

if(AfxMessageBox(str,MB_YESNO|MB_DEFBUTTON1|MB_ICONINFORMATION)==IDYES)

{

::SendMessage(m_hWnd,WM_MESSAGE,1,bSucess);//发送消息

}

else

{

::SendMessage(m_hWnd,WM_MESSAGE,0,bSucess);//发送消息

}

}

else

{

;

}

}

//得到幸运数字

intCResultThread::GetLuckNum(void)

{

srand((unsigned)time(NULL));

returnrand()%10;

}

结果预览:

工程源码下载地址:

/detail/cbnotes/4958041

欢迎大家修改和指正。

注意事项:

(1)从CWinThread类派生的类的构造函数和解析函数默认是protected,要采用第二种方法则要修改为public,否则将出错。

(2)注意线程参数的传递方法,和前面不一样的。即创建线程时先设置为CREATE_SUSPENDED(挂起),然后设置相关参数,然后再启动线程,具体可以参考程序。

m_pResultThread=newCResultThread;

m_pResultThread->CreateThread(CREATE_SUSPENDED);//创建线程时先挂起

m_pResultThread->m_hWnd=m_hWnd;//然后相关设置参数

m_pResultThread->ResumeThread();//再运行

(3)注意CWinThread派生类中Run()函数的重载。仔细想想下面的程序的执行原理,为什么对话框结束后就会执行此处。如果将对话框设置为非模式对话框,还会正常吗?

intCInputThread::Run()

{

// TODO:在此添加专用代码和/或调用基类

m_pResultThread->PostThreadMessage(WM_THREADMESSAGE,0,m_InputDlg.m_nInput);

returnCWinThread::Run();

}

Fr:/cbNotes

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。