MFC的進程和線程_第1頁
MFC的進程和線程_第2頁
MFC的進程和線程_第3頁
MFC的進程和線程_第4頁
MFC的進程和線程_第5頁
已閱讀5頁,還剩43頁未讀, 繼續免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

第11章MFC的進程和線程本章講述多線程編程技術。11.1Win32的進程和線程概念首先有必要了解一下進程和線程的概念。11.1.1進程的概念進程的定義是為執行程序指令的線程而保留的一系列資源的集合。進程是一個可執行的程序,由私有虛擬地址空間、代碼、數據和其他操作系統資源(如進程創建的文件、管道、同步對象等)組成。進程是一些所有權的集合,一個進程擁有內存,CPU運行時間等一系列資源,為線程的運行提供一個環境,每個進程都有它自己的地址空間和動態分配的內存,以及線程,文件和其他一些模塊。11.1.2線程的概念一個應用程序可以有一個或多個進程,一個進程可以有一個或多個線程,其中一個是主線程。線程是操作系統分時調度分配CPU時間的基本實體。一個線程可以執行程序的任意部分的代碼,即使這部分代碼被另一個線程并發地執行;一個進程的所有線程共享它的虛擬地址空間、全局變量和操作系統資源。11.2進程編程因為MFC沒有提供類處理進程,所以直接使用了Win32API函數。11.2.1進程的創建調用CreateProcess函數創建新的進程,運行指定的程序。CreateProcess的原型如下:BOOLCreateProcess(LPCTSTRlpApplicationName,LPTSTRlpCommandLine,LPSECURITY_ATTRIBUTESlpProcessAttributes,LPSECURITY_ATTRIBUTESlpThreadAttributes,BOOLbInheritHandles,DWORDdwCreationFlags,LPVOIDlpEnvironment,LPCTSTRlpCurrentDirectory,LPSTARTUPINFOlpStartupInfo,LPPROCESS_INFORMATIONlpProcessInformation);11.2.2進程的管理和終止取得當前進程的句柄和ID需要以下兩個函數:HANDLEGetCurrentProcess(void)DWORDGetCurrentProcessId(void)11.2.3取得和設置進程的優先級取得一個進程的優先級的函數如下:DWORDGetPriorityClass(HANDLEhProcess);其中,參數hProcess是要取得優先級的進程的句柄。設置一個進程的優先級的函數如下: BOOLSetPriorityClass(HANDLEhProcess,DWORDdwPriorityClass);11.2.4進程的終止終止一個進程有兩種方法:最常用的方法是調用函數ExitProcess()結束進程。另一種方法是調用函數TerminateProcess終止進程。在當前進程中的一個線程調用函數ExitProcess就會結束當前進程。VOIDExitProcess(UNITuExitCode);當需要在當前進程中結束其他進程時,就需要用到另一種方法即調用函數TerminateProcess,其函數原形為:VOIDTerminateProcess(UNITuExitCode);11.2.5判斷一個進程是否終止當一個進程終止或結束時就不能利用這個進程的句柄再對該進程進行操作,這就需要判斷一個進程是否終止,或者要看一個進程是否正常的退出,也需要查看這個進程的返回碼??梢岳煤瘮礕etExitCodeProcess來判斷一個進程是否終止,如果終止,則取得這個進程的返回碼,否則返回標志STILL_ACTIVE。BOOLGetExitCodeProcess(HANDLEhProcess,LPDWORDlpExitCode);11.3Win32中關于多線程的幾個函數下表11.1列出了Win32中關于多線程的幾個函數CreateThread創建一個新線程CreatRemoteThread在另一個進程中創建一個新線程ExitThread正常的結束一個線程的執行TerminateThread終止一個線程的執行GetExitCodeThread得到另一個線程的退出碼GetThreadPriority得到線程的優先級SetThreadPriority設置一個線程的優先級SuspendThread掛起一個線程ResumeThread重啟一個線程CloseHand關閉一個線程的句柄11.3.1線程的創建使用CreateThread函數創建線程,CreateThread的原型如下:HANDLECreateThread(LPSECURITY_ATTRIBUTESlpThreadAttributes,DWORDdwStackSize,LPTHREAD_START_ROUTINElpStartAddress,LPVOIDlpParameter,DWORDdwCreationFlags,//creationflagsLPDWORDlpThreadId);11.3.2CreatRemoteThread函數該函數在其他進程的虛擬地址空間創建線程。其聲明如下:HANDLECreatRemoteThread(HANDLEhProcess;LPSECURITY_ATTRIBUTESlpThreadAttributes,DWORDdwStackSize,LPTHREAD_START_ROUTINElpStartAddress,LPVOIDlpParameter,DWORDdwCreationFlags,//creationflagsLPDWORDlpThreadId);11.3.3SuspendThread和

ResumeThread函數SuspendThread()的作用為暫停一個線程,ResumeThread()的作用為重啟一個線程。它們都帶有一個HANDLE型的參數,參數值為要暫?;蛑貑⒕€程的句柄。SuspendThread(HANDLEhThread);ResumeThread(HANDLEhThread);11.3.4ExitThread和TerminateThread函數終止一個線程有兩種方法:最常用的方法是調用函數ExitThread()結束線程。另一種方法是調用函數TerminateThread終止線程。在當前線程中的一個線程調用函數ExitProcess就會結束當前線程:VOIDExitThread(DWORDdwExitCode);這個函數用來結束當前線程,其中參數用來存放此線程的退出碼。這是最正常的結束線程的方法。BOOLTerminateThread(HANDLEhThread.//線程句柄DWORDdwExitCode//線程退出碼);11.3.5取得一個線程的優先級的函數獲得一個線程的優先級的函數:intGetThreadPriority(HANDLEhThread);參數hThread是要取得優先級的線程的句柄。設置一個線程的優先級的函數:BOOLSetThreadPriority(HANDLEhThread,intnPriority);參數hThread是要取得優先級線程的句柄,參數nPriority就是要設置的優先級。11.4MFC中多線程的實現在Win32API的基礎之上,MFC提供了處理線程的類和函數。MFC對多線程進行一種簡單的封裝,其中每個線程都是從CWinThread類繼承而來的。每一個應用程序的執行都有一個主線程,主線程也是從CWinThread類繼承而來的。可以利用CWinThread對象創建應用程序執行的其他線程。處理線程的類是CWinThread,它的成員變量m_hThread和m_hThreadID是對應的Win32線程句柄和線程ID。MFC多線程編程中經常用到的幾個全局函數是AfxBeginThread、AfxEndThread等。MFC明確區分兩種線程:用戶界面線程(Userinterfacethread)和工作者線程(Workerthread)。用戶界面線程一般用于處理用戶輸入并對用戶產生的事件和消息作出應答。工作者線程用于完成不要求用戶輸入的任務,如實時數據采集、計算等。11.4.1與多線程編程相關的全局函數AfxBeginThread用戶界面線程和工作者線程都是由AfxBeginThread創建的。:用戶界面線程的AfxBeginThread的原型如下:CWinThread*AFXAPIAfxBeginThread(CRuntimeClass*pThreadClass,intnPriority,UINTnStackSize,DWORDdwCreateFlags,LPSECURITY_ATTRIBUTESlpSecurityAttrs)11.4.1與多線程編程相關的全局函數(續)工作者線程的AfxBeginThread的原型如下:CWinThread*AFXAPIAfxBeginThread(AFX_THREADPROCpfnThreadProc,LPVOIDpParam,intnPriority,UINTnStackSize,DWORDdwCreateFlags,LPSECURITY_ATTRIBUTESlpSecurityAttrs)11.4.1與多線程編程相關的全局函數(續)CWinThread*AFXAPIAfxGetThread();該全局函數用來獲取線程對象。VOIDAFXAPIAfxEndThread(UNITnExitCode,BOOLbDelete=TRUE);該函數用來結束線程的執行。VOIDAFXAPIAfxInitThread();初始化進程函數。VOIDAFXAPIAfxTermThread(HINSTANCEhInstTerm=NULL);終止線程執行函數。11.4.2CWinThread類CWinThread類封裝了上節講的API函數,并且增加了新的函數和屬性。至于其類的具體聲明在afxwin.h中。11.4.3工作者線程的創建工作線程經常來完成一些后臺工作,如計算,打印等,這樣用戶就不必因為計算機在從事繁雜而耗時的工作而等待。需要向AfxBeginThread()函數提供線程函數的起始地址和傳給線程函數的參數。線程函數的格式如下:UNIT函數名(LPVOIDpParam)11.4.4創建用戶界面線程用戶界面線程的創建有兩種方法。方法一是首先從CWinThread類派生一個類(需要用宏DECLARE_DYNCREATE和IMPLEMENT_DYNCREATE對該類進行聲明和實現),然后調用函數AfxBeginThread()創建CWinThread派生類的對象進行初始化,啟動線程執行。方法二先通過構造函數創建類的一個對象,然后由程序員調用函數CWinThread::CreateThread來啟動線程。通常CWinThread類的對象在該線程的生存期結束時將自動終止,如果程序員希望自己來控制,則需要將m_AutoDelete設為FALSE。這樣在線程終止之后,CWinThread類對象仍然存在,此時需要手動刪除CWinThread對象。11.5線程之間的通信線程通信一般有四種方式:全局變量方式,消息傳遞方式,參數傳遞方式和線程同步方式。全局變量方式:在一個進程中共享全局變量就可以通過全局變量來進行線程間的通信參數傳遞方式:主線程在創建子進程的時候,可以通過傳給線程函數的參數和其通信。所傳遞的參數是一個32位的指針,該指針可以指向簡單的數據,也可以指向結構甚至更復雜的數據類型。通過參數的傳遞能在兩個線程函數之間傳遞很復雜的數據。消息傳遞法:通過函數在主線程和工作線程之間傳遞消息,通過函數在用戶界面線程和其他線程之間傳遞消息,消息傳遞是一種很重要的線程之間的通信方式。線程之間通信的一種重要的方法就是線程同步,將在下一節給予介紹。11.6線程的調度和同步Win32提供了一組對象用來實現多線程的同步。它們是Critical_section(關鍵段),Event(事件),Mutex(互斥對象),Semaphores(信號量)。MFC封裝了這幾個同步對象,它們分別是:CCritical_section、Cevent、Cmutex、Csemaphores。這四個同步類都以CsyncObject為它們的父類。11.6.1臨界段對象臨界段對象一次只允許一個線程取得一個數據區進行操作。這時候可以創建臨界段對象,并且使用這個臨界段對象進行相應的操作以實現線程的同步。定義一個臨界段對象。臨界段對象的變量類型是CRITICAL_SECTION:CRITICAL_SECTION對象名;然后調用函數InitializeCriticalSection初始化該對象。初始化時把對象設置為NOT_SINGALED,表示允許線程使用資源:函數說明如下:InitializeCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);11.6.1臨界段對象(續)進入和離開臨界區。如果一段程序代碼需要對某個資源進行同步保護,則這是一段臨界段代碼。在進入該關鍵段代碼前調用EnterCriticalSection函數,這樣,其他線程都不能執行該段代碼,若它們試圖執行就會被阻塞。完成關鍵段的執行之后,調用LeaveCriticalSection函數,其他的線程就可以繼續執行該段代碼。如果該函數不被調用,則其他線程將無限期的等待。VOIDEnterCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);VOIDLeaveCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);兩個函數的參數lpCriticalSection是指向CRITICAL_SECTION結構的指針。11.6.2互斥對象互斥對象的工作方式和臨界段對象非常相似,其區別在于互斥量不僅保護一個進程內的共享資源,而且保護系統中進程之間的資源,它是通過互斥量提供一個互斥量名來實現進程和線程之間共享協調的。在使用互斥量進行同步線程前,必須首先創建互斥量可以調用CreateMutex函數創建互斥量,其函數說明如下:HANDLECreateMutex(LPSECURITY_ATTRIBUTESlpMutexAttributes,BOOLbInitialOwner;LPCSTRlpName);11.6.2互斥對象(續)打開互斥量的函數 OpenMutex( DWORDdwDesiredAccess,BOOLbInitialOwner;LPCSTRlpName );獲得互斥量: 獲得互斥量的函數如下: DWORDSignalObjectAndWait( HANDLEhObjectToSignal,HANDLEhObjectToWaitOn, DWORDdwMilliseconds,BOOLbAlertable );釋放互斥量:調用函數RealseMutex可以釋放互斥量:BOOLRealseMutex(HANDLEhMutex);11.6.3事件對象事件和互斥量的區別如下:事件主要用于協調兩個和多個線程之間的動作,使其協調一致,符合邏輯。一個線程等待某個事件的發生,另一個線程則在某個事件發生后產生一個信號,通知正在等待的線程,而互斥量主要是保證任一時刻只有一個線程在使用共享的資源,什么時刻運行哪個線程是隨機的,是由操作系統決定的,用戶沒有任何決定權,所以互斥量不能使兩個線程按一定順序執行。有信號和無信號的含義不同:對于互斥量來講,有信號狀態就是指線程正在擁有互斥量,其他線程不能獲得互斥量,無信號是指沒有線程擁有互斥量,其他線程可以獲得互斥量,訪問被互斥量保護的資源。對于事件而講,當等待的事件發生時,事件對象處于有信號狀態;相反當等待的事件沒有發生時,稱事件處于無信號狀態。11.6.3事件對象(續)創建和打開事件對象: 在利用事件之前,必須先調用CreateEvent函數創建一個事件對象 HANDLECreateEvent( LPSECURITY_ATTRIBUTESlpEventAttributes,BOOLbInitialState;BOOLbManulReset;LPCSTRlpName ); HANDLEOpenEvent( DWORDdwDesiredAccess,BOOLbInitialHandle;LPCSTRlpName );設置和重置事件對象: BOOLSetEvent(HANDLEhEvent); 該函數觸發一個事件,即將事件置為有信號狀態。 BOOLResetEvent(HANDLEhEvent); 該函數將一個事件對象重置為無信號狀態。 BOOLPlusEvent(HANDLEhEvent);最后,使用CloseHandle銷毀創建的事件對象。11.6.4信號量對象在線程之間進行同步的原因大致有兩個:一個是由于線程之間競爭共享的資源,一個是為了完成某種任務而協作。通過互斥可以實現線程之間由于競爭所需要的同步,通過事件可以實現線程之間由于協作而需要的同步。原則上講,使用互斥量和事件可以解決所有線程之間的同步問題。而信號量很好的將互斥量和事件結合起來,同時解決了線程的競爭和協作的問題。它是對事件同步的推廣——在信號量之中有一個內置的計數值,用于對資源進行計數。同時它通過內置的互斥機制保證在有多個線程試圖對計數值進行修改時,在任何一個時刻只有一個線程對計數值進行修改。11.6.4信號量對象(續)創建和打開信號量對象: 首先在使用信號量對象,必須調用函數CreateSemaphore創建它。 HANDLECreateSemaphore( LPSECURITY_ATTRIBUTESlpSemaphoreAttributes, BOOLbInitialCount LONGlMaximumCount; LPCSTRlpName }11.6.4信號量對象(續)可以調用函數OpenSemaphore來獲得信號量句柄: HANDLEOpenSemaphore{ DWORDwDesiredAccess, BOOLbInitialHandle; LPCSTRlpName };獲得和釋放信號量, BOOLRealseSemaphore{ HANDLEhSemaphore, LONGlRealseCount, LPLONGlpPreviousCount, };當這個信號量對象不再需要時,就應該調用函數CloseHandle來釋放這個信號量對象,從內存中消除。11.6.5各種同步方法的比較 本章講述了四種線程同步方法,它們分別使互斥量,臨界段,事件和信號量,這些方法有各自的特點,用于不同的場合。下面比較一下這些方法的異同:互斥量、事件和信號量都是內核對象,可用于進程之間的同步;臨界段是進程內對象只能用于線程之間的同步。雖然在一個進程內實現同步時,臨界段對象和互斥量相似,但是在性能上臨界段對象要優于互斥量。事件和其他幾個同步方法的不同在于事件的主要作用不是保護進程共享資源,而是用于等待某個事件和特定事件發生時發送信號,以協調線程之間的動作。信號量和其他同步方法的區別在于它允許一個以上的線程同時訪問共享的資源,而其他同步的方法都保證同時只能有一個線程訪問共享的資源。信號量的主要功能在于用于資源計數。同步方法的選擇要根據應用場合、同步目的和各種同步對象各自的特點來選擇,下面介紹選擇同步方法的一些原則,可以根據這些原則來選擇同步對象:首先線程在訪問共享資源之前是否要等待某個事件的發生。比如,在線程訪問一個共享文件前是否要從通信端口接收信息。如果是這樣,就可以用事件同步;在一個應用程序中是否有多個線程可以同時訪問的共享資源,如果是則選擇信號量;是否有多個進程使用共享資源,如果是則選擇互斥量;如果以上條件都不滿足,則使用臨界段就可以了。11.7應用實例排火車的游戲大家都玩過吧?其規則是這樣的:將撲克牌分成兩份,每人拿一份,二人輪流出牌。如果你出的牌和前面的某張牌一樣(不區分花色),則從那張一樣的牌到你出的牌都被你拿走,這樣輪流出牌,直到其中一人的牌出完為止。最后手上有牌的一方為勝,圖11.1。在這個程序中要用到事件同步。因為人和計算機不能同時出牌。比較合理的做法是人出完牌后通知計算機,然后計算機出牌,計算機出完牌之后通知人,計算機處于等待狀態。11.7應用實例(續) 首先創建基于對話框工程Eg11_1:1.在VC++集成開發環境中,通過菜單File|New,彈出New對話框;2.在Projects選項卡中選擇MFCAppWizard(exe),在Projectname中輸入“Eg11_1”Location讀者可以自己選擇;3.按下OK按鈕,在彈出的MFCAppWizardStep-1對話框中選擇程序框架為單文檔框架,即選中SingleDocument;4.一直接受默認選項,直到MFCAppWizardStep-6,把CEg11_1View類的基類選擇為CFormView。如圖11.2所示。5.按下“Finish”按鈕,在彈出的NewProjectInformation對話框中按下“OK”按鈕后等待創建完相應的工程。11.7.1用戶界面的設計可以參照圖11.1和表11.2來設計界面11.7.2新增成員變量及初始化在CEg11_1View.h中增加一些新的變量:public: CEg11_1Doc*GetDocument();

//事件對象,表示計算機出牌完成,等待游戲者出牌CEventm_computerready; //事件對象,表示游戲者出牌完成,等待計算機出牌 CEventm_playerready; //游戲正在進行的標志 BOOLm_playing; //當前牌局中牌的張數 intm_cardnum; //當前計算機成績即計算機牌的張數 intm_computernum; //當前游戲者牌的牌的張數 intm_playernum; //當前牌局 intm_card[100];11.7.2新增成員變量及初始化(續)在構造函數中初始化事件對象和其他變量:CEg11_1View::CEg11_1View() :CFormView(CEg11_1View::IDD),m_computerready(TRUE),m_playerready(FALSE){ //{{AFX_DATA_INIT(CEg11_1View) //NOTE:theClassWizardwilladdmemberinitializationhere //}}AFX_DATA_INIT //TODO:addconstructioncodeherem_playing=false;m_cardnum=0;m_computernum=24;m_playernum=24;}11.7.2新增成員變量及初始化(續)通過ClassWizard添加成員函數OnInitialUpdate(),重載這個函數如下:voidCEg11_1View::OnInitialUpdate(){ CFormView::OnInitialUpdate(); GetParentFrame()->RecalcLayout(); ResizeParentToFit();

GetDlgItem(IDC_DISCARD)->EnableWindow(FALSE); SetDlgItemText(IDC_STATIC_SCORE1,"24"); SetDlgItemText(IDC_STATIC_SCORE2,"24");}11.7.3創建菜單響應函數通過ClassWizard為菜單ID_FILE_BEGIN和ID_FILE_END添加菜單響應函數,接受系統默認的函數名,編輯函數代碼如下:voidCEg11_1View::OnFileBegin(){ //TODO:Addyourcommandhandlercodehere

m_playing=true; //啟動計算機出牌線程和游戲者線程 AfxBeginThread(CompThread,(void*)this); AfxBeginThread(PlayerThread,(void*)this);

}voidCEg11_1View::OnFileEnd(){ //TODO:Addyourcommandhandlercodehere

exit(0

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論