VS2010/MFC入門程式設計十七(多執行緒的建立,包括工作執行緒和使用者介面執行緒)
1.MFC多執行緒簡介
MFC對多執行緒進行了一層簡單的封裝,在Visual C++中每個執行緒都是從CWinThread類繼承而來的。每一個應用程式的執行都有一個主執行緒,這個主執行緒也是從CWinThread類繼承而來的。可以利用CWinThread物件建立應用程式執行的其它執行緒。
MFC用CWinThread物件來表示所有執行緒。利用MFC可以建立兩種執行緒,分別稱之為工作者執行緒和使用者介面執行緒。二者的主要區別在於工作者執行緒沒有訊息迴圈,而使用者介面執行緒有自己的訊息佇列和訊息迴圈。工作者執行緒沒有訊息機制,通常用來執行後臺計算和維護任務,如冗長的計算過程,印表機的後臺列印等。使用者介面執行緒一般用於處理獨立於其他執行緒執行之外的使用者輸入,響應使用者及系統所產生的事件和訊息等。但對於
2.MFC多執行緒基礎
為了深入瞭解MFC下建立執行緒的方法,我們先深入學習一下CWinThread類。CWinThread類在MFC類結構中的位置如下圖所示:
圖1:CWinThread類在mfc類結構中的位置
首先看一下類CWinThread的宣告。類CWinThread的宣告在afxwin.h中:
Class CWinThread :publicCCmdTarget { DECLARE_DYNAMIC(CWinThread) public: //建構函式 CWinThread(); //用來具體建立執行緒的函式 BOOL CreateThread(DWORDdwCreateFlags = 0,UINTnStackSize = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs= 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) BOOL m_bAutoDelete; // enables'delete this' after thread termination // only valid while running //該執行緒的控制代碼 HANDLE m_hThread; // thisthread's HANDLE operator HANDLE() const; //該執行緒的ID DWORD m_nThreadID; // thisthread's ID //執行緒優先順序 int GetThreadPriority(); BOOL SetThreadPriority(int nPriority); // Operations //掛起執行緒 DWORD SuspendThread(); //啟動執行緒 DWORD ResumeThread(); //傳送執行緒訊息 BOOL PostThreadMessage(UINT message, WPARAM wParam, LPARAM lParam); // Overridables // 執行緒初始化,每個應用程式都可以過載該函式 virtual BOOL InitInstance(); // running and idle processing virtual int Run(); virtual BOOL PreTranslateMessage(MSG*pMsg); virtual BOOL PumpMessage(); // low level message pump virtual BOOL OnIdle(LONGlCount);// return TRUEif more idle processing virtual BOOL IsIdleMessage(MSG*pMsg); // checks for special messages // thread termination virtual int ExitInstance(); //default will 'delete this' // Advanced: exception handling virtual LRESULT ProcessWndProcException(CException*e,constMSG*pMsg); // Advanced: handling messages sent to message filter hook virtual BOOL ProcessMessageFilter(intcode,LPMSGlpMsg); // Advanced: virtual access to m_pMainWnd virtual CWnd* GetMainWnd(); // Implementation public: virtual ~CWinThread(); #ifdef _DEBUG virtual void AssertValid() const; virtual void Dump(CDumpContext&dc)const; #endif void CommonConstruct(); virtual void Delete(); // 'delete this' only if m_bAutoDelete == TRUE public: // constructor used by implementation of AfxBeginThread CWinThread(AFX_THREADPROCpfnThreadProc,LPVOIDpParam); // valid after construction LPVOID m_pThreadParams;// generic parameters passed to starting function AFX_THREADPROC m_pfnThreadProc; // set after OLE is initialized void (AFXAPI*m_lpfnOleTermOrFreeLib)(BOOL,BOOL); COleMessageFilter* m_pMessageFilter; protected: BOOL DispatchThreadMessageEx(MSG* msg); // helper void DispatchThreadMessage(MSG* msg); // obsolete };
有些函式是不是很眼熟呀,在前面的文章中已經介紹和使用過啦。MFC類就是這樣的,它無非就是簡單封裝一些API函式,並新增一些自己的函式而構成的。不用MFC我們照樣可以編寫很優秀的程式,MFC的宗旨就是簡化程式設計,讓你可以很容易入門和簡單的使用,也催生了大量的程式設計師。但對喜歡刨根問底的朋友卻是一道很厚的牆。
下面幾個函式是多執行緒程式設計中經常用到的幾個全域性函式:
//建立工作執行緒
CWinThread* AFXAPIAfxBeginThread( <span style="white-space:pre"> </span>AFX_THREADPROC pfnThreadProc,//執行緒函式 <span style="white-space:pre"> </span>LPVOID pParam,//傳給執行緒函式的引數 <span style="white-space:pre"> </span>int nPriority =THREAD_PRIORITY_NORMAL,//執行緒的優先順序 <span style="white-space:pre"> </span>UINT nStackSize = 0,//堆疊大小 <span style="white-space:pre"> </span>DWORD dwCreateFlags = 0,//建立起始狀態標誌 <span style="white-space:pre"> </span>LPSECURITY_ATTRIBUTES lpSecurityAttrs= NULL//執行緒的安全屬性 ); //建立使用者介面執行緒 CWinThread* AFXAPIAfxBeginThread( <span style="white-space:pre"> </span>CRuntimeClass* pThreadClass,//從CWinThread派生的類的RUNTIME_CLASS <span style="white-space:pre"> </span>int nPriority =THREAD_PRIORITY_NORMAL,//執行緒的優先順序 <span style="white-space:pre"> </span>UINT nStackSize = 0,// 堆疊大小 <span style="white-space:pre"> </span>DWORD dwCreateFlags = 0,// 建立起始狀態標誌 <span style="white-space:pre"> </span>LPSECURITY_ATTRIBUTESlpSecurityAttrs =NULL//執行緒的安全屬性 ); //獲取執行緒物件 CWinThread* AFXAPIAfxGetThread(); //獲取當前訊息 MSG* AFXAPIAfxGetCurrentMessage(); //結束執行緒執行 void AFXAPIAfxEndThread(UINTnExitCode,BOOLbDelete =TRUE); //初始化執行緒 void AFXAPIAfxInitThread(); //終止執行緒執行 void AFXAPIAfxTermThread(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* AFXAPI AfxGetThread()
{
// check for current thread in module thread state
AFX_MODULE_THREAD_STATE* pState= AfxGetModuleThreadState();
CWinThread* pThread= pState->m_pCurrentWinThread;
return pThread;
}
MSG* AFXAPI AfxGetCurrentMessage()
{
<span style="white-space:pre"> </span>_AFX_THREAD_STATE* pState= AfxGetThreadState();
<span style="white-space:pre"> </span>ASSERT(pState);
<span style="white-space:pre"> </span>return &(pState->m_msgCur);
}
CWinThread* AFXAPI AfxBeginThread(AFX_THREADPROC pfnThreadProc, LPVOID pParam,int nPriority,UINT nStackSize, DWORD dwCreateFlags,
LPSECURITY_ATTRIBUTESlpSecurityAttrs)
{
#ifndef _MT
<span style="white-space:pre"> </span>pfnThreadProc;
<span style="white-space:pre"> </span>pParam;
<span style="white-space:pre"> </span>nPriority;
<span style="white-space:pre"> </span>nStackSize;
<span style="white-space:pre"> </span>dwCreateFlags;
<span style="white-space:pre"> </span>lpSecurityAttrs;
<span style="white-space:pre"> </span>return NULL;
#else
<span style="white-space:pre"> </span>ASSERT(pfnThreadProc!=NULL);
<span style="white-space:pre"> </span>CWinThread* pThread=DEBUG_NEWCWinThread(pfnThreadProc,pParam);
<span style="white-space:pre"> </span>ASSERT_VALID(pThread);
<span style="white-space:pre"> </span>if (!pThread->CreateThread(dwCreateFlags|CREATE_SUSPENDED,nStackSize,
lpSecurityAttrs))
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>pThread->Delete();
<span style="white-space:pre"> </span>return NULL;
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>VERIFY(pThread->SetThreadPriority(nPriority));
<span style="white-space:pre"> </span>if (!(dwCreateFlags&CREATE_SUSPENDED))
VERIFY(pThread->ResumeThread() != (DWORD)-1);
<span style="white-space:pre"> </span>return pThread;
#endif //!_MT)
}
CWinThread* AFXAPI AfxBeginThread(CRuntimeClass*pThreadClass,
int nPriority, UINT nStackSize,DWORD dwCreateFlags,
LPSECURITY_ATTRIBUTES lpSecurityAttrs)
{
#ifndef _MT
pThreadClass;
nPriority;
nStackSize;
dwCreateFlags;
lpSecurityAttrs;
return NULL;
#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();
return NULL;
}
VERIFY(pThread->SetThreadPriority(nPriority));
if (!(dwCreateFlags&CREATE_SUSPENDED))
{
ENSURE(pThread->ResumeThread() != (DWORD)-1);
}
return pThread;
#endif //!_MT
}
void AFXAPI AfxEndThread(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
LRESULT CALLBACK _AfxMsgFilterHook(intcode,WPARAMwParam,LPARAMlParam);
void AFXAPI AfxInitThread()
{
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());
}
}
extern CThreadSlotData* _afxThreadData;
void AFXAPI AfxTermThread(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();
delete pModuleThreadState->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();
}
void CWinThread::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_ATTRIBUTES lpSecurityAttrs)
{
#ifndef _MT
dwCreateFlags;
nStackSize;
lpSecurityAttrs;
return FALSE;
#else
ENSURE(m_hThread==NULL); // already created?
// setup startup structure for thread initialization
_AFX_THREAD_STARTUP startup;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);
return FALSE;
}
// 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);
return FALSE;
}
// 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);
return FALSE;
}
// allow thread to continue, once resumed (it may alreadybe resumed)
VERIFY(::SetEvent(startup.hEvent2));
return TRUE;
#endif //!_MT
}
void CWinThread::Delete()
{
// delete thread if it is auto-deleting
if (m_bAutoDelete)
delete this;
}
在建立一個新的執行緒時,不必直接建立執行緒物件,因為執行緒物件是由全域性函式AxCreateThread()自動產生的。只要首先定義一個CWinThread類指標,然後呼叫全域性函式AxCreateThread()來產生一個新的執行緒物件,並呼叫執行緒類的CreateThread()成員函式來具體建立執行緒,最後將新的執行緒物件的指標返回。應該將其存在CWinThread變數中,以便能夠進一步控制執行緒。
AxCreateThread()函式和CWinThread:: CreateThread()函式並不是簡單地對_beginthreadex()函式進行了一下封裝,它還做了一些應用程式框架所需的內部資料的初始化工作,並保證使用正確的C執行庫版本,因為在兩個函式一開始就要檢查環境引數_MT是否已經定義。而且在建立執行緒過程中還進行了很多檢查和測試,保證能夠正確產生新的執行緒,同時線上程建立失敗時正確地釋放掉已經分配的資源。
注意事項:
(1)一般情況下,推薦使用AfxBeginThread()來一次性地建立並啟動一個執行緒,但是也可以通過兩步法來建立執行緒:首先建立CWinThread類的一個物件,然後呼叫該物件的成員函式CreateThread()來啟動該執行緒。
(2)MFC支援兩類多執行緒,即工作執行緒和使用者介面執行緒。使用者介面執行緒經常過載InitInstance()和ExitInstance()函式,用以控制使用者介面執行緒例項的初始化和必要的清理工作。工作者執行緒一般不使用。
3.執行緒函式
(1)工作執行緒的執行緒函式:從AfxBeginThread()函式的引數可以看出:
AFX_THREADPROC pfnThreadProc,//執行緒函式
LPVOID pParam, //傳給執行緒函式的引數
其中AFX_THREADPROC為一個巨集,其定義如下:
typedef UINT(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檔案
UINT ThreadFunc(LPVOID pParm);//執行緒函式的定義
bool IsPrime(UINT Number ); //素數判斷
struct threadInfo
{
unsigned int nRange;//範圍
HWND hWnd;//主視窗控制代碼,用於訊息的傳送
};
private:
CWinThread *m_pThread;
//.cpp檔案
//開始
void CAfxBeginThread1Dlg::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;
}
}
UINT ThreadFunc(LPVOID pParm)
{
threadInfo *pInfo=(threadInfo*)pParm;
bool bResult = 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);
return 0;
}
//素數判斷
bool IsPrime(UINT nNumber )
{//定理:如果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)
return false;
}
return true;
}
//顯示訊息處理函式
LRESULT CAfxBeginThread1Dlg::OnMyInfo(WPARAMwParam,LPARAMlParam)
{
if (wParam == 0&&lParam == -1)
{//結束
m_stTip.SetWindowText("完成");
GetDlgItem(IDC_BUTTON_CAL)->EnableWindow(TRUE);
}
else
{//是素數
m_Progress.SetPos(lParam);
CString str;
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);
}
}
return 0;
}
程式碼下載地址:http://download.csdn.net/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檔案
define STEPLEN 1024*64 //檔案一次讀寫的進步k
UINT ThreadFunc(LPVOID pParm);//執行緒函式的定義:檔案複製執行緒
CWinThread *pUIThread;//介面執行緒,用於進度的顯示
CWinThread *pThread; //工作執行緒,用於檔案的複製
CString m_szSrcPath; //原始檔路徑
CString m_szDesPath; //目標檔案路徑
//.CPP檔案
//檔案複製模組:
void CAfxBeginThead2Dlg::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);//啟動
}
//檔案複製執行緒
UINT ThreadFunc(LPVOID pParm)
{
CAfxBeginThead2Dlg*pInfo=(CAfxBeginThead2Dlg*)pParm;
//開啟原始檔,得到檔案的大小,並一塊一塊的讀取
CFile ReadFile;
BOOL bOpen = 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);
return 1;
}
//得到檔案的大小,用於計算進度
DWORD dwTotalSize= ReadFile.GetLength();
DWORD dwCompleteSize= 0;//已完成的大小
//計算檔案去讀的步長
DWORD dwStep = STEPLEN > dwTotalSize? dwTotalSize : STEPLEN;
//資料緩衝區
char *pBuf =newchar[dwStep+1];
memset(pBuf,0x00,dwStep+1);
DWORD dwRead = dwStep;
//建立目標檔案,若目標檔案存在則清空
CFile WriteFile;
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);
return 2;
}
//檔案的複製:從原始檔去讀取資料,並寫入到目標檔案中
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));
}
//完成
delete pBuf;
//關閉檔案
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);
return 0;
}
//使用者介面執行緒類:派生於CWinThread類
//檔案複製模組:標頭檔案
class CcbCopyFile : publicCWinThread
{
DECLARE_DYNCREATE(CcbCopyFile)
protected:
CcbCopyFile(); // 動態建立所使用的受保護的建構函式
virtual ~CcbCopyFile();
CString m_szSrcPath,m_szDesPath;
public:
CCopyFileDlg *m_pProgressDlg;//進度介面
virtual BOOL InitInstance();
virtual int ExitInstance();
afx_msg void OnThreadInfo(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()
{
}
BOOL CcbCopyFile::InitInstance()
{
// TODO: 在此執行任意逐執行緒初始化
return TRUE;
}
int CcbCopyFile::ExitInstance()
{
// TODO: 在此執行任意逐執行緒清理
return CWinThread::ExitInstance();
}
BEGIN_MESSAGE_MAP(CcbCopyFile,CWinThread)
ON_THREAD_MESSAGE(WM_THREADINFO,&CcbCopyFile::OnThreadInfo)
END_MESSAGE_MAP()
// CcbCopyFile 訊息處理程式
//顯示訊息處理函式
void CcbCopyFile::OnThreadInfo(WPARAMwParam,LPARAMlParam)
{
if (wParam == 0)
{//原始檔路徑引數
m_szSrcPath.Format("%s",lParam);
//AfxMessageBox(m_szSrcPath);
}
else if (wParam == 1)
{//目標檔案路徑引數
m_szDesPath.Format("%s",lParam);
//AfxMessageBox(m_szDesPath);
}
else if (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);
}
else if (wParam == 3)
{//進度
m_pProgressDlg->m_Progress.SetPos(lParam);
}
else if (wParam == 4)
{//速度
m_pProgressDlg->UpdateSpeed(lParam);
}
else if (wParam == 5)
{//時間
float *p = (float *)lParam;
m_pProgressDlg->UpdateTime(*p);
}
else
{//完成
m_pProgressDlg->OnCancel();
}
//return 0;
}
工程原始碼下載地址:http://download.csdn.net/detail/cbnotes/4956549
相關推薦
VS2010/MFC入門程式設計十七(多執行緒的建立,包括工作執行緒和使用者介面執行緒)
1.MFC多執行緒簡介 MFC對多執行緒進行了一層簡單的封裝,在Visual C++中每個執行緒都是從CWinThread類繼承而來的。每一個應用程式的執行都有一個主執行緒,這個主執行緒也是從CWinThread類繼承而來的。可以利用CWinThread物件建立應用程式執行的其它執行緒。 MFC用CW
Java(二分查詢演算法實現,分別使用遞迴和非遞迴方式)
public class BinarySearch { private int[] array; private int index; private int min; private int max; public BinarySearch(int[]
VS2013/MFC程式設計入門之二十七(常用控制元件:列表檢視控制元件List Control 上)
本節為大家詳解列表檢視控制元件List Control的使用。 列表檢視控制元件簡介 列表檢視控制元件List Control同樣比較常見,它能夠把任何字串內容以列表的方式顯示出來,這種顯示方式的特點是整潔、直觀,在實際應用中能為使
VS2013/MFC程式設計入門之七(對話方塊:建立對話方塊類和新增控制元件變數)
前兩講中為大家講解了如何建立對話方塊資源。建立好對話方塊資源後要做的就是生成對話方塊類了。再宣告下,生成對話方塊類主要包括新建對話方塊類、新增控制元件變數和控制元件的訊息處理函式等。 因為給大家的例程Addition是基於對話方塊的程式,所以程式自動建立了
VS2013/MFC程式設計入門之十九(常用控制元件:靜態文字框)
從本節開始講解各種常用控制元件的用法。MFC中常用控制元件主要包括:靜態文字框、編輯框、單選按鈕、複選框、分組框、列表框、組合框、圖片控制元件、列表控制元件、樹形控制元件和進度條控制元件等等。本節先來講解靜態文字框的使用。 控制元件的通知訊息
VS2013/MFC程式設計入門之十六(對話方塊:檔案對話方塊)
上一講介紹的是訊息對話方塊,本節講解檔案對話方塊。 檔案對話方塊的分類 檔案對話方塊分為開啟檔案對話方塊和儲存檔案對話方塊,相信大家在Windows系統中經常見到這兩種檔案對話方塊。例如,很多編輯軟體像記事本等都有“開啟”選項,選擇“開啟”後會彈出一個對
VS2013/MFC程式設計入門之十二(對話方塊:屬性頁對話方塊及相關類的介紹)
本節開始講一種特殊的對話方塊--屬性頁對話方塊。 屬性頁對話方塊的分類 屬性頁對話方塊想必大家並不陌生,XP系統中桌面右鍵點屬性,彈出的就是屬性頁對話方塊,它通過標籤切換各個頁面。另外,我們在建立MFC工程時使用的嚮導對話方塊也屬於屬性頁對
VS2013/MFC程式設計入門之十五(對話方塊:訊息對話方塊)
本節講解Windows系統中最常用最簡單的一類對話方塊--訊息對話方塊。 我們在使用Windows系統的過程中經常會見到訊息對話方塊,提示我們有異常發生或提出詢問等。因為在軟體開發中經常用到訊息對話方塊,所以MFC提供了兩個函式可以直接生成指
VS2013/MFC程式設計入門之十一(對話方塊:非模態對話方塊的建立及顯示)
上一節講了模態對話方塊及其彈出過程,本節接著講另一種對話方塊--非模態對話方塊的建立及顯示。 前面已經說過,非模態對話方塊顯示後,程式其他視窗仍能正常執行,可以響應使用者輸入,還可以相互切換。本文中將上一講中建立的Tip模態對話方塊改為非模態對話方塊,讓
周威學Go從入門到放棄第十篇(多執行緒)
go語言多執行緒開發特別容易入門,通過go關鍵字實現,程式碼如下: package main import ( "fmt" "runtime" ) func say(s string) { for i := 0; i < 5; i++ { runtime
tensorflow入門教程(十七)python3網路爬蟲(上)
1、概述 在繼續學習tensorflow之前,我想先寫一下python爬蟲的內容,作為深度學習的一個技能補充。深度學習需要用到大量的訓練資料,沒有爬蟲靠人工下載,工作量不敢想象。學會爬蟲就可以去爬一些收集訓練資料需要(或喜歡)的網站了。如果想深入學習爬蟲,推薦這本書《精
[WebGL入門]二十七,多紋理
注:文章譯自http://wgld.org/,原作者杉本雅広(doxas),文章中如果有我的額外說明,我會加上[lufy:],另外,鄙人webgl研究還不夠深入,一些專業詞語,如果翻譯有誤,歡迎大家指正。使用多個紋理上次介紹了WebGL中的紋理的使用方法。簡單的實現了將紋理貼
Spring入門第二十七課
url cati urn ksh base stock XML repo study 聲明式事務 直接上代碼: db.properties jdbc.user=root jdbc.password=logan123 jdbc.driverClass=com.mysql.j
ES6-----學習系列十七(Decorator)
col 修飾符 script hello fun only 概念 new ret Decorator就是修飾器的意思 一、概念 修飾器是一個函數,用來修改類的行為(註意:1、函數 2、修改行為 3、對類進行操作) 1、只讀的修飾器 { let reado
MyBatis基礎入門《十七》動態SQL
MyBatis基礎入門《十七》動態SQL 描述: >> 完成多條件查詢等邏輯實現 >> 用於實現動態SQL的元素主要有: > if > trim > where > set > choose(
《深入淺出MFC》第十四章 多線程程序設計
同步 相關 註意 RKE 變量 大堆 操作系統 使用 判斷 模塊表示一段可執行的程序,其程序、數據、資源都被加載到內存中,由系統構建一個數據結構來管理它,就是一個模塊,這個數據結構被稱作MDB。進程是一大堆擁有權的集合,進程擁有地址控件,動態配置的內存、文件、線程、和一系列
機器學習筆記(十九):TensorFlow實戰十一(多執行緒輸入資料)
1 - 引言 為了加速模型訓練的時間,TensorFlow提供了一套多執行緒處理輸入資料的框架。 下面我們來詳細的介紹如何使用多執行緒來加速我們的模型訓練速度 2 - 佇列與多執行緒 在TensorFlow中,佇列和變數類似,我們可以修改它們的狀態。下面給出一個示例來展示如
c++程式設計習慣七(不要讓解構函式丟擲異常)
c++之中,異常處理是允許解構函式丟擲異常的,但是,並不鼓勵那樣做,考慮一下這種情況: class Student{ ... ~Student();//在這裡吐出一個異常 }; void dosomething() { std::vector<
LNMP架構二十七(php-fpm程序管理)【完】
二十七、php-fpm程序管理 1、php-fpm的程序管理有兩種模式 php-fpm的程序數也是可以根據設定分為動態和靜態的。 靜態模式:直接開啟指定數量的php-fpm程序,不再增加或者減少;動態模式:開始的時候開啟一定數量的php-fpm程序,當請求量變大的時候,動態的增加php-
WiFi-ESP8266入門開發(十七)--MQTT客戶端
注:對於ESP8266開源技術感興趣的可以加群,我們一起探索交流學習,群號:579932824。群名:ESP8266開源技術交流群。 介紹 MQTT是基於輕量級的基於釋出 - 訂閱的訊息傳遞協議。 它比其他基於請求響應的API(如HTTP)更快(更快)。 它是基於TC