




已閱讀5頁,還剩25頁未讀, 繼續(xù)免費(fèi)閱讀
版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
MFC下的多線程編程,作者:陳帥,2008年7月30日,一、MFC 支持的兩種線程:,1. 用戶界面線程,通常用于處理用戶輸入及響應(yīng)用戶生成的事件和消息,并獨(dú)立地相應(yīng)正在應(yīng)用程序其他部分執(zhí)行的線程產(chǎn)生的消息和時(shí)間,并包含一個(gè)消息泵(a Message Pump)。用戶界面線程包含一個(gè)消息處理的循環(huán),以應(yīng)對各種事件。,對于用戶來說工作線程運(yùn)行在后臺(tái)。這就使得工作線程特別適合去等待一個(gè)事件的發(fā)生。,2. 工作線程,工作線程適用于處理那些不要求用戶輸入并且比較消耗時(shí)間的其 他任務(wù)(如大規(guī)模的重復(fù)計(jì)算,網(wǎng)絡(luò)數(shù)據(jù)的發(fā)送與接受)。,注意:,在MFC應(yīng)用程序中,所有的線程都是由CWinThread對象來表示的; CWinThread是用戶接口線程的基類,CWinApp就是CWinThread派生出來的,在編寫用戶接口線程時(shí),也需要從CWinThread 類派生出自己的線程類; CWinThread同樣是工作線程的基類,但在編寫工作線程的時(shí)候,升值不必刻意地從CWinThread類派生出自己的線程類對象。用戶可以調(diào)用MFC框架的AfxBeginThread幫助函數(shù),會(huì)創(chuàng)建CWinThread對象。 在Win32API中不區(qū)分兩種線程,它只需要知道線程的起始地址,就可以開始執(zhí)行線程。,3.創(chuàng)建MFC的工作線程,(1).編程實(shí)現(xiàn)控制函數(shù),一個(gè)工作線程對應(yīng)一個(gè)控制函數(shù)。線程執(zhí)行的任務(wù)都應(yīng)編寫在控制函數(shù)之中。編寫實(shí)現(xiàn)工作線程的控制函數(shù)是創(chuàng)建工作線程的第一步。,控制函數(shù)的原型聲明是:UNIT ControlFunctionName(LPVOID pParam); 其中, UNIT ControlFunctionName:是控制函數(shù)的名字,自定。,參數(shù)pParam:是一個(gè)32位指針值,是啟動(dòng)工作線程時(shí),有調(diào)用的AfxBeginThread()函數(shù)傳遞給工作線程的控制函數(shù)的。這個(gè)值既可以是指向簡單數(shù)據(jù)類型的指針,用來傳遞int之類的數(shù)值,也可是是指向包含了許多參數(shù)的結(jié)構(gòu)體或其他對象的指針;甚至可以忽略它。,(2).創(chuàng)建并啟動(dòng)工作線程,在進(jìn)程的主線程或其他線程中調(diào)用AfxBeginThread()函數(shù)就可以創(chuàng)建新的線程,并使線程開始運(yùn)行。,AfxBeginThread()函數(shù)是MFC提供的幫助函數(shù),有兩個(gè)重載版本,區(qū)別在于使用的入口參數(shù)不同。一個(gè)用于創(chuàng)建并啟動(dòng)用戶接口線程,一個(gè)用于創(chuàng)建并啟動(dòng)工作線程。,要?jiǎng)?chuàng)建并啟動(dòng)工作線程,必須采用如下的調(diào)用格式:,CWinThread* AfxBeginThread(,AFX_THREADPROC pfnThreadProc,LPVOID pParam, int nPriority = THREAD_PRIORITY_NORMAL, UINT nStackSize = 0, DWORD dwCreateFlags = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL );,參數(shù)pfnThreadProc:是一個(gè)指向工作線程的控制函數(shù)的指針,即控制函數(shù)的地址。創(chuàng)建工作線程是必須指定將在此線程內(nèi)部運(yùn)行的控制函數(shù)。,參數(shù)pParam:是一個(gè)指向某種類型的數(shù)據(jù)結(jié)構(gòu)指針,執(zhí)行本函數(shù)時(shí),將把這個(gè)指針進(jìn)一步傳遞給此線程的控制函數(shù),使之成為線程控制函數(shù)的入口參數(shù)。,參數(shù)nPriority:通常設(shè)為0。,參數(shù)nStackSize:通常設(shè)為0。,參數(shù)dwCreateFlags :通常設(shè)為0。,參數(shù)dwCreateFlags :通常設(shè)為0。,參數(shù)lpSecurityAttrs:通常設(shè)為NULL。,(3)創(chuàng)建工作線程的例子,struct int n; double* pD; myData; myData ss;/定義了該類型的變量,對該變量的初始化的代碼省略了 UNIT MyCalcFunc(LPVOID pParam) /如果入口函數(shù)為空指針,終止線程 if( pParam=NULL) AfxEndThread(MY_NULL_POINTTER_ERROR); int n=pParam-n; /數(shù)組元素個(gè)數(shù); double* pD=pParam-pD; /指向數(shù)組的第一個(gè)元素; double sum=0; /數(shù)組元素之和; for(int i=0; in; i+) sum+=pDi;/數(shù)組之和; CString bb; bb.Format(“數(shù)組的和是:%d“,sum);/格式化顯示字符串; AfxMessageBox(bb); /顯示結(jié)果; return 0; ,(i) 編程實(shí)現(xiàn)線程控制函數(shù),(2)在程序進(jìn)程的主線程中調(diào)用AfxBeginThread()函數(shù)來創(chuàng)建并啟動(dòng)運(yùn)行這個(gè)線程。將控制函數(shù)名和結(jié)構(gòu)變量的地址作為參數(shù)來傳遞,其他的參數(shù)省略,表示使用默認(rèn)值。,AfxBeginThread(MyCalcFunc,一旦調(diào)用了此函數(shù),線程就被創(chuàng)建,并開始執(zhí)行線程函數(shù)。當(dāng)數(shù)據(jù)的計(jì)算完成時(shí),函數(shù)將停止運(yùn)行,線程擁有的堆棧和其它的資源都將釋放。CWinThread對象將被刪除。,3. 創(chuàng)建并啟動(dòng)用戶界面線程,創(chuàng)建并啟動(dòng)用戶界面線程一般要經(jīng)過3個(gè)步驟: 第一步是從CWinThread類派生出自己的線程類; 第二步是改造這個(gè)線程類,使它能夠完成用戶所希望的工作; 第三步是創(chuàng)建并啟動(dòng)用戶界面線程。,(1)從CWinThread類派生出自己的線程類,要?jiǎng)?chuàng)建一個(gè)MFC的用戶界面線程,所要做的第一件事就是從CWinThread類派生出自己的線程類,一般借助ClassWizard來做這項(xiàng)工作。,(2)改造自己的線程類,(i)在這個(gè)線程類的.h頭文件中,使用DECLARE_DYNCRATE宏來聲明這個(gè)類;在用戶線程類的.cpp實(shí)現(xiàn)文件中,使用IMPLEMENT_DYNCREATE宏來實(shí)現(xiàn)這個(gè)類。,前者的調(diào)用格式是: DECLARE_DYNCRATE(class_name),其中class_name中是實(shí)際的類名。,(ii)如果在一個(gè)類中宣布使用了DECLARE_DYNCRATE宏,那么就必須在這個(gè)類的.cpp實(shí)現(xiàn)文件中,使用IMPLEMENT_DYNCREATE宏。它的調(diào)用格式是: IMPLEMENT_DYNCREATE(class_name, base_class_name) 參數(shù)是實(shí)際的線程類名和它的基類名。,(iii)這個(gè)線程類必須重載它的基類(CWinThread類)的某些成員函數(shù),如該類的InitInstance()成員函數(shù);對于基類的其它成員函數(shù),可以有選擇的重載,也可以使用缺省函數(shù)。,創(chuàng)建用戶界面線程時(shí)相關(guān)成員函數(shù)的重載,(vi) 創(chuàng)建新的用戶界面窗口類,如窗口、對話框,并添加所需要的用戶界面控件,然后建立新建的線程類與這些用戶界面窗口類的聯(lián)系。,(v)利用類向?qū)?,為新建的線程類添加控件成員變量,添加響應(yīng)消息的成員函數(shù),為它們編寫實(shí)現(xiàn)的代碼,經(jīng)過以上步驟的改造,用戶的線程類已經(jīng)具備了完成用戶任務(wù)的能力.,(3)創(chuàng)建并啟用用戶界面線程,要?jiǎng)?chuàng)建并啟動(dòng)用戶界面線程,可以使用MFC提供的AfxBeginThread()函數(shù)的另一個(gè)版本,其格式是:,CWinThread* AfxBeginThread( CRuntimeClass* pThreadClass, int nPriority = THREAD_PRIORITY_NORMAL, UINT nStackSize = 0, DWORD dwCreateFlags = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL );,參數(shù)pThreadClass:是一個(gè)指向CRuntimeClass類對象的指針,該類是從CWinThread類繼承的。用戶界面線程運(yùn)行時(shí)類就在第一步驟從CWinThread派生的線程類,本參數(shù)就指向它,在實(shí)際調(diào)用時(shí),一般使用RUNTIME_CLASS 宏將線程類指針轉(zhuǎn)化為指向CRuntimeClass對象的指針。 其他的參數(shù)這和創(chuàng)建啟動(dòng)工作線程時(shí)一樣。,4.終止線程,(1) 正常終止線程,VOID PostQuitMessage()函數(shù)的調(diào)用格式是: VOID PostQuitMessage(int nExitCode); 參數(shù)nExitCode是一個(gè)整數(shù)型值,指定一個(gè)應(yīng)用程序的終止代碼。,PostQuitMessage()函數(shù)發(fā)送一個(gè)WM_QUIT消息到線程的消息隊(duì)列,并立即返回,沒有返回值。函數(shù)只是簡單地告訴系統(tǒng),這個(gè)線程要求終止。當(dāng)線程從它的消息隊(duì)列收到一個(gè)WM_QUIT消息時(shí),會(huì)退出它的消息循環(huán),并將控制權(quán)返回給系統(tǒng),同時(shí)把WM_QUIT消息的wParam參數(shù)中的終止代碼也返回給系統(tǒng),線程也就終止了。,(2)提前終止線程,要想在線程尚未完成它的工作時(shí)提前終止線程,只需從線程內(nèi)調(diào)用AfxEndThread函數(shù),就可以強(qiáng)迫線程終止。此函數(shù)的調(diào)用格式是:,Void AfxEndThread(UNIT nExitCode);,參數(shù)nExitCode指定了線程的終止代碼。,執(zhí)行此函數(shù)將停止函數(shù)所在線程的執(zhí)行,撤銷該線程的堆棧,解除所有綁定到此線程動(dòng)態(tài)鏈接庫DLLs,并從內(nèi)存中刪除此線程。,(3)終止線程另一種方法:,使用Win32 API 提供的TerminateThread()函數(shù),也可以用來終止一個(gè)正在運(yùn)行的線程,但是它產(chǎn)生的后果是不可預(yù)料的,一般僅用來終止堆棧中的死線程,而且此函數(shù)本身不做任何內(nèi)存的清除工作。,5.MFC下多線程的同步機(jī)制,(1)基本概念: 在程序中使用多線程時(shí),一般很少有多個(gè)線程能在其生命期內(nèi)進(jìn)行完全獨(dú)立的操作。更多的情況是一些線程進(jìn)行某些處理操作,而其他的線程必須對其處理結(jié)果進(jìn)行了解。正常情況下對這種處理結(jié)果的了解應(yīng)當(dāng)在其處理任務(wù)完成后進(jìn)行。,下述對象是用來支持同步的: 1)信號量 2)互斥鎖 3)臨界區(qū) 4)事件,1)信號量,為了限制使用共享資源的線程數(shù)目,我們應(yīng)該使用信號量。信號量是一個(gè)內(nèi)核對象。它存儲(chǔ)了一個(gè)計(jì)數(shù)器變量來跟蹤使用共享資源的線程數(shù)目。例如,下面代碼使用CSemaphore類創(chuàng)建了一個(gè)信號量對象,它確保在給定的時(shí)間間隔內(nèi)(由構(gòu)造函數(shù)第一個(gè)參數(shù)指定)最多只有5個(gè)線程能使用共享資源。還假定初始時(shí)沒有線程獲得資源:,CSemaphore g_Sem(5, 5);,一旦線程訪問共享資源,信號量的計(jì)數(shù)器就減1.若變?yōu)?,則接下來對資源的訪問會(huì)被拒絕,直到有一個(gè)持有資源的線程離開(也就是說釋放了信號量)。我們可以如下使用:,/ Try to use the shared resource :WaitForSingleObject(g_Sem, INFINITE); / Now the users counter of the semaphore has decremented by one / Use the shared resource / After we done, let other threads use the resource :ReleaseSemaphore(g_Sem, 1, NULL); / Now the users counter of the semaphore has incremented by one,互斥鎖設(shè)計(jì)為對同步訪問共享資源進(jìn)行保護(hù)?;コ怄i在內(nèi)核中實(shí)現(xiàn),因此需要進(jìn)入內(nèi)核模式操縱它們?;コ怄i不僅能在不同線程之間,也可以在不同進(jìn)程之間進(jìn)程同步。要跨進(jìn)程使用,則互斥鎖應(yīng)該是有名的。MFC中使用CMutex類來操縱互斥鎖??梢匀缦路绞绞褂茫?2)互斥鎖,CSingleLock singleLock( ,3)臨界區(qū),臨界區(qū)(Critical Section)是一段獨(dú)占對某些共享資源訪問的代碼,在任意時(shí)刻只允許一個(gè)線程對共享資源進(jìn)行訪問。如果有多個(gè)線程試圖同時(shí)訪問臨界區(qū),那么在有一個(gè)線程進(jìn)入后其他所有試圖訪問此臨界區(qū)的線程將被掛起,并一直持續(xù)到進(jìn)入臨界區(qū)的線程離開。臨界區(qū)在被釋放后,其他線程可以繼續(xù)搶占,并以此達(dá)到用原子方式操作共享資源的目的。,臨界區(qū)在使用時(shí)以CRITICAL_SECTION結(jié)構(gòu)對象保護(hù)共享資源,并分別用EnterCriticalSection()和LeaveCriticalSection()函數(shù)去標(biāo)識(shí)和釋放一個(gè)臨界區(qū)。所用到的CRITICAL_SECTION結(jié)構(gòu)對象必須經(jīng)過InitializeCriticalSection()的初始化后才能使用,而且必須確保所有線程中的任何試圖訪問此共享資源的代碼都處在此臨界區(qū)的保護(hù)之下。否則臨界區(qū)將不會(huì)起到應(yīng)有的作用,共享資源依然有被破壞的可能。,MFC為臨界區(qū)提供有一個(gè)CCriticalSection類,使用該類進(jìn)行線程同步處理是非常簡單的,只需在線程函數(shù)中用CCriticalSection類成員函數(shù)Lock()和UnLock()標(biāo)定出被保護(hù)代碼片段即可。,如:/ MFC臨界區(qū)類對象 CCriticalSection g_clsCriticalSection; / 共享資源 char g_cArray10; UINT ThreadProc20(LPVOID pParam) / 進(jìn)入臨界區(qū) g_clsCriticalSection.Lock(); / 對共享資源進(jìn)行寫入操作 for (int i = 0; i 10; i+) g_cArrayi = a; Sleep(1); / 離開臨界區(qū) g_clsCriticalSection.Unlock(); return 0; ,4)事件,一般來說,事件用于這樣的情形下:當(dāng)指定的動(dòng)作發(fā)生后,一個(gè)線程(或多個(gè)線程)才開始執(zhí)行其任務(wù)。例如,一個(gè)線程可能等待必需的數(shù)據(jù)收集完后才開始將其保存到硬盤上。,有兩種事件:手動(dòng)重置型和自動(dòng)重置型。通過使用事件,我們可以輕松地通知另一個(gè)線程特定的動(dòng)作已經(jīng)發(fā)生了。對于手動(dòng)重置型事件,線程使用它通知多個(gè)線程特定動(dòng)作已經(jīng)發(fā)生,而對于自動(dòng)重置型事件,線程使用它只可以通知一個(gè)線程。,在MFC中,CEvent類封裝了事件對象(若在win32中,它是用一個(gè)HANDLE來表示的)。CEvent的構(gòu)造函數(shù)運(yùn)行我們選擇創(chuàng)建手動(dòng)重置型和自動(dòng)重置型事件。默認(rèn)的創(chuàng)建類型是自動(dòng)重置型事件。為了通知正在等待的線程,我們可以調(diào)用CEvent:SetEvent方法,這個(gè)方法將會(huì)讓事件進(jìn)入已通知狀態(tài)。若事件是手動(dòng)重置型,則事件會(huì)保持已通知狀態(tài),直到對應(yīng)的CEvent:ResetEvent被調(diào)用,這個(gè)方法將使得事件進(jìn)入未通知狀態(tài)。這個(gè)特性使得一個(gè)線程可以通過一個(gè)SetEvent調(diào)用去通知多個(gè)線程。若事件是自動(dòng)重置型,則所有正在等待的線程中只有一個(gè)線程會(huì)接收到通知。當(dāng)那個(gè)線程接收到通知后,事件會(huì)自動(dòng)進(jìn)入未通知狀態(tài)。,CEvent g_eventStart; UINT ThreadProc1(LPVOID pParam) :WaitForSingleObject(g_eventStart, INFINITE); return 0; UINT ThreadProc2(LPVOID pParam) :WaitForSingleObject(g_eventStart, INFINITE); return 0; ,在這個(gè)例子中,一個(gè)全局的
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025至2030年中國大型混料桶數(shù)據(jù)監(jiān)測研究報(bào)告
- 2025年消防設(shè)施操作員之消防設(shè)備基礎(chǔ)知識(shí)能力測試試卷A卷附答案
- 2025年軍隊(duì)文職人員招聘之軍隊(duì)文職法學(xué)題庫練習(xí)試卷B卷附答案
- 電動(dòng)葫蘆考試試題及答案
- 酒店洗滌合同(2篇)
- 餐飲業(yè)服務(wù)培訓(xùn)試卷
- 中學(xué)生課外閱讀指南經(jīng)典情節(jié)讀后感
- 十萬個(gè)為什么科學(xué)故事讀后感
- 秦文字從大篆到小篆的演變
- 山東省濱州市2024-2025學(xué)年高一上學(xué)期1月期末生物學(xué)試題(含答案)
- 2023年高考真題全國乙卷物理試卷
- 新疆省新疆生產(chǎn)建設(shè)兵團(tuán)2025屆小升初數(shù)學(xué)高頻考點(diǎn)檢測卷含解析
- 專題46:地理意義類綜合題之產(chǎn)業(yè)集聚的意義(原卷版)-備戰(zhàn)2021屆高考地理二輪復(fù)習(xí)題型專練
- 節(jié)后復(fù)工復(fù)產(chǎn)安全教育培訓(xùn)資料
- 2025年安徽省合肥熱電集團(tuán)招聘50人歷年高頻重點(diǎn)模擬試卷提升(共500題附帶答案詳解)
- 煤礦監(jiān)測監(jiān)控培訓(xùn)
- 柔性電路板自動(dòng)化制造-深度研究
- 2024年河南建筑職業(yè)技術(shù)學(xué)院高職單招職業(yè)技能測驗(yàn)歷年參考題庫(頻考版)含答案解析
- 電纜故障知識(shí)培訓(xùn)課件
- 國家開放大學(xué)本科《商務(wù)英語4》一平臺(tái)機(jī)考真題及答案(第四套)
- 交通運(yùn)輸考試題及答案
評論
0/150
提交評論