




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
1、第22章進(jìn)程、線程與并行計(jì)算進(jìn)程是正在運(yùn)行的程序,線程是輕量級(jí)的進(jìn)程。多任務(wù)的并發(fā)執(zhí)行會(huì)用到多線程(multithreading),而CPU的多核(mult-core)化又將原來只在巨型機(jī)和計(jì)算機(jī)集群中才使用的并行計(jì)算帶入普通PC應(yīng)用的多核程序設(shè)計(jì)中。本章先介紹進(jìn)程與線程的概念和編程,再給出并行計(jì)算的基本概念和內(nèi)容。下一章討論基于多核CPU的并行計(jì)算的若干具體編程接口和方法。22.1 進(jìn)程與線程進(jìn)程(process)是執(zhí)行中的程序,線程(thread)是一種輕量級(jí)的進(jìn)程。22.1.1 進(jìn)程與多任務(wù)現(xiàn)代的操作系統(tǒng)都是多任務(wù)(multitask)的,即可同時(shí)運(yùn)行多個(gè)程序。進(jìn)程(process)是位
2、于內(nèi)存中正被CPU運(yùn)行的可執(zhí)行程序?qū)嵗瑓⒁妶D22-1。進(jìn)程(內(nèi)存中)程序=4t運(yùn)行可執(zhí)行文件(盤上)圖22-1程序與進(jìn)程目前的主流計(jì)算機(jī)采用的都是馮諾依曼(JohnvonNeumann)體系結(jié)構(gòu)存儲(chǔ)程序計(jì)算模型。程序(program)是在內(nèi)存中順序存儲(chǔ)并以線性模式在CPU中串行執(zhí)行的指令序列。對(duì)于傳統(tǒng)的單核CPU計(jì)算機(jī),多任務(wù)操作系統(tǒng)的實(shí)現(xiàn)是通過CPU分時(shí)(time-sharing)和程序并發(fā)(concurrency)完成的。即在一個(gè)時(shí)間段內(nèi),操作系統(tǒng)將CPU分配給不同的程序,雖然每一時(shí)刻只有一個(gè)程序在CPU中運(yùn)行,但是由于CPU的速度非??欤诤芏痰臅r(shí)間段中可在多個(gè)進(jìn)程間進(jìn)行多次切換,所以
3、用戶的感覺就像多個(gè)程序在同時(shí)執(zhí)行,我們稱之為多任務(wù)的并發(fā)。22.1.2 進(jìn)程與線程程序一般包括代碼段、數(shù)據(jù)段和堆棧,對(duì)具有GUI(GraphicalUserInterfaces,圖形用戶界面)的程序還包含資源段。進(jìn)程(process)是應(yīng)用程序的執(zhí)行實(shí)例,即正在被執(zhí)行的程序。每個(gè)進(jìn)程都有自己的虛擬地址空間,并擁有操作系統(tǒng)分配給它的一組資源,包括堆棧、寄存器狀態(tài)等。線程(thread)是CPU的調(diào)度單位,是進(jìn)程中的一個(gè)可執(zhí)行單元,是一條獨(dú)立的指令執(zhí)行路徑。線程只有一組CPU指令、一組寄存器和一個(gè)堆棧,它本身沒有其他任何資源,而是與擁有它的進(jìn)程共享幾乎一切,包括進(jìn)程的數(shù)據(jù)、資源和環(huán)境變量等。線程的
4、創(chuàng)建、維護(hù)和管理給操作系統(tǒng)的負(fù)擔(dān)比進(jìn)程要小得多,所以才叫輕量級(jí)的進(jìn)程(lightweightprocess)。一個(gè)進(jìn)程可以擁有多個(gè)線程,而一個(gè)線程只能屬于一個(gè)進(jìn)程。每個(gè)進(jìn)程至少包含一個(gè)線程一一主線程,它負(fù)責(zé)程序的初始化工作,并執(zhí)行程序的起始指令。隨后,主線程可為執(zhí)行各種不同的任務(wù)而分別創(chuàng)建多個(gè)子線程,參見圖22-2。并行任務(wù)j詐行柢翦£并行任務(wù)3主城程并行任落L并行仟?jiǎng)軪井行任芳3主域程4*mA圖22-2多線程(分叉-接合)圖示其中:A為主線程,B、C、D皆為A的子線程;不同并行任務(wù)中的同名子線程可以互不相同一個(gè)程序的多個(gè)運(yùn)行,可以通過啟動(dòng)該程序的多個(gè)實(shí)例(即多個(gè)進(jìn)程)來完成,也可以
5、只運(yùn)行該程序的一個(gè)實(shí)例(一個(gè)進(jìn)程),而由該進(jìn)程創(chuàng)建多個(gè)線程來做到。顯然后者要比前者更高效,更能節(jié)約系統(tǒng)的有限資源。這對(duì)需要在同一時(shí)段響應(yīng)成千上萬個(gè)用戶請(qǐng)求的Web服務(wù)器程序和網(wǎng)絡(luò)數(shù)據(jù)庫(kù)管理程序等來說是至關(guān)重要的。有關(guān)進(jìn)程和線程的進(jìn)一步內(nèi)容,可以閱讀與操作系統(tǒng)相關(guān)的圖書資料。22.1.3 多線程編程的困難因?yàn)橥怀绦颍ㄟM(jìn)程)的多個(gè)線程共享同樣的數(shù)據(jù)和資源,所以會(huì)出現(xiàn)同步、排隊(duì)和競(jìng)爭(zhēng)等問題,可能導(dǎo)致死鎖、無限延遲和數(shù)據(jù)競(jìng)爭(zhēng)等現(xiàn)象的發(fā)生,這些都需要我們?cè)诔绦蛑屑右越鉀Q。MFC雖然提供了一個(gè)線程類和若干同步類,但是仍然屬于線程的低級(jí)編程,既困難又繁瑣。利用.NET框架類庫(kù)中的線程命名空間下的線程類,則
6、可以簡(jiǎn)化線程編程。22.2 傳統(tǒng)的進(jìn)程和線程編程使用傳統(tǒng)的Win32/64API和MFC直接進(jìn)行進(jìn)程和線程編程異常復(fù)雜和繁瑣,需要程序員自己處理線程間的同步、互斥、死鎖等具體問題。22.2.1 創(chuàng)建、管理和終止進(jìn)程由于MFC中并沒有提供處理進(jìn)程的類,所以我們需要直接使用Windows的API函數(shù)來創(chuàng)建、管理和終止進(jìn)程。1 .創(chuàng)建進(jìn)程下面的CreateProcess函數(shù)用于在當(dāng)前進(jìn)程中創(chuàng)建一個(gè)新進(jìn)程(和其主線程),以運(yùn)行指定(路徑/文件名或命令行)的應(yīng)用程序:BOOLCreateProcess(/成功返回非0,失敗返回0(可用GetLastError函數(shù)返回出錯(cuò)代碼)LPCTSTRlpAppli
7、cationName,可執(zhí)行文件的全路徑或文件名,有命令行時(shí)可為NULLLPTSTRlpCommandLine,/命令行參數(shù)字符串,有可應(yīng)用名時(shí)可為NULLLPSECURITY_ATTRIBUTESlpProcessAttributes,/進(jìn)程的安全屬性,NULL表默認(rèn)安全LPSECURITY_ATTRIBUTESlpThreadAttributes,/主線程的安全屬性,NULL表默認(rèn)安全BOOLbInheritHandles,/子進(jìn)程是否繼承新進(jìn)程的句柄DWORDdwCreationFlags,/創(chuàng)建標(biāo)志,用于設(shè)置進(jìn)程的創(chuàng)建狀態(tài)和優(yōu)先級(jí)別,可為0LPVOIDlpEnvironment,/環(huán)境
8、變量,為NULL時(shí)同當(dāng)前進(jìn)程的LPCTSTRlpCurrentDirectory,/進(jìn)程運(yùn)行的當(dāng)前目錄,為NULL時(shí)同當(dāng)前進(jìn)程的LPSTARTUPINFOlpStartupInfo,/指向設(shè)置進(jìn)程主窗口或控制條的各種屬性的結(jié)構(gòu)指針LPPROCESS_INFORMATIONlpProcessInformation/指向返回進(jìn)程信息的結(jié)構(gòu)指針);其中,啟動(dòng)信息結(jié)構(gòu)STARTUPINFO和進(jìn)程信息結(jié)構(gòu)PROCESS_INFORMATION的定義分別為:typedefstruct_STARTUPINFODWORDcb;/結(jié)構(gòu)的長(zhǎng)度(字節(jié)數(shù))LPTSTRlpReserved;/保留,必須為NULLLPT
9、STRIpDesktop;/桌面-窗口站的名稱LPTSTRIpTitle;/控制臺(tái)進(jìn)程的標(biāo)題DWORDdwX;/窗口位置的橫坐標(biāo)DWORDdwY;/窗口位置的縱坐標(biāo)DWORDdwXSize;/窗口的水平尺寸DWORDdwYSize;/窗口的垂直尺寸DWORDdwXCountChars;/控制臺(tái)窗口的屏幕緩沖區(qū)寬度(字符數(shù))DWORDdwYCountChars;/控制臺(tái)窗口的屏幕緩沖區(qū)高度(字符數(shù))DWORDdwFillAttribute;/控制臺(tái)窗口的初始文本和背景色DWORDdwFlags;/窗口的創(chuàng)建標(biāo)志W(wǎng)ORDwShowWindow;/用作窗口顯示函數(shù)ShowWindow的缺省參數(shù)WOR
10、DcbReserved2;/保留,必須為0LPBYTElpReserved2;/保留,必須為NULLHANDLEhStdInput;/標(biāo)準(zhǔn)輸入的句柄HANDLEhStdOutput;/標(biāo)準(zhǔn)輸出的句柄HANDLEhStdError;/標(biāo)準(zhǔn)錯(cuò)誤的句柄STARTUPINFO,*LPSTARTUPINFO;typedefstruct_PROCESS_INFORMATIONHANDLEhProcess;/返回的進(jìn)程句柄HANDLEhThread;/返回的主線程句柄DWORDdwProcessId;/返回的進(jìn)程IDDWORDdwThreadId;/返回的主線程IDPROCESS_INFORMATION,*
11、LPPROCESS_INFORMATION;2 .管理進(jìn)程管理進(jìn)程包括獲取進(jìn)程的句柄和ID、獲取與設(shè)置進(jìn)程的優(yōu)先級(jí)、等待進(jìn)程返回等操作。1)獲取進(jìn)程的句柄和ID除了可從創(chuàng)建進(jìn)程函數(shù)CreateProcess的最后一個(gè)(返回)參數(shù)一一進(jìn)程信息結(jié)構(gòu)PROCESS_INFORMATION變量來獲取所創(chuàng)建的新進(jìn)程及其主線程的句柄和ID外,還可利用API函數(shù)GetCurrentProcess和GetCurrentProcessId來獲取當(dāng)前進(jìn)程的句柄和ID:HANDLEGetCurrentProcess(void);DWORDGetCurrentProcessId(void);不過用GetCurrent
12、Process返回的是一個(gè)偽句柄,只能在當(dāng)前進(jìn)程中使用??梢哉{(diào)用API函數(shù)DuplicateHandle將此偽句柄轉(zhuǎn)換為一個(gè)真正的句柄。2)獲取和設(shè)置進(jìn)程的優(yōu)先級(jí)在Windows操作系統(tǒng)中,進(jìn)程有6種優(yōu)先級(jí)別(prioritylevel/class),從低到高分別為:空閑(Idel)、低普通(Belownormal)、普通(Normal)、高普通(Abovenormal)、高(High)和實(shí)時(shí)(Realtime),對(duì)應(yīng)的符號(hào)常量和數(shù)值見表22-1。表22-1進(jìn)程優(yōu)先級(jí)符號(hào)常量?jī)?yōu)先級(jí)符號(hào)常量對(duì)應(yīng)數(shù)值高實(shí)時(shí)REALTIME_PRIORITY_CLASS0x00000100高HIGH_PRIORIT
13、Y_CLASS0x00000080|1高普通ABOVE_NORMAL_PRIORITY_CLASS0x00008000|普通NORMAL_PRIORITY_CLASS0x00000020J低低普通BELOW_NORMAL_PRIORITY_CLASS0x00004000空閑IDLE_PRIORITY_CLASS0x00000040可以在創(chuàng)建新進(jìn)程時(shí),利用其創(chuàng)建標(biāo)志參數(shù)dwCreationFlags來設(shè)置。也可以用API函數(shù)GetPriorityClass和SetPriorityClass來獲取和設(shè)置指定進(jìn)程的優(yōu)先級(jí):DWORDGetPriorityClass(HANDLEhProcess);B
14、OOLSetPriorityClass(HANDLEhProcess,DWORDdwPriorityClass);例如:DWORDp=GetPriorityClass(GetCurrentProcess();SetPriorityClass(GetCurrentProcess(),IDLE_PRIORITY_CLASS);3)等待進(jìn)程返回可以調(diào)用API函數(shù)WaitForSingleObject來等待指定進(jìn)程(或線程)結(jié)束后返回:DWORDWINAPIWaitForSingleObject(HANDLEhHandle,DWORDdwMilliseconds);例如:WaitForSingleOb
15、ject(pi.hProcess,INFINITE);3 .結(jié)束進(jìn)程結(jié)束進(jìn)程的方法有多種,可以調(diào)用API函數(shù)ExitProcessk來結(jié)束當(dāng)前進(jìn)程(及其所有線程)(對(duì)控制臺(tái)程序,在接收到CTRL+C或CTRL+BREAK信號(hào)后會(huì)調(diào)用此函數(shù)):VOIDExitProcess(UINTuExitCode);或調(diào)用API函數(shù)ExitProcess來結(jié)束指定進(jìn)程(及其所有線程):BOOLTerminateProcess(HANDLEhProcess,UINTuExitCode);進(jìn)程的所有線程終止后進(jìn)程也會(huì)自動(dòng)終止。在用戶關(guān)閉系統(tǒng)或注銷退出系統(tǒng)時(shí),也會(huì)導(dǎo)致進(jìn)程終止。在結(jié)束進(jìn)程后,還需要調(diào)用API的Cl
16、oseHandle函數(shù)來刪除進(jìn)程和線程對(duì)象:BOOLCloseHandle(HANDLEhObject);例如:CloseHandle(pi.hProcess);CloseHandle(pi.hThread);4 .例子下面是一個(gè)控制臺(tái)程序,在該程序中,創(chuàng)建一個(gè)新進(jìn)程來運(yùn)行另一個(gè)指定的可執(zhí)行程序。為此,需要?jiǎng)?chuàng)建一個(gè)名為Process的“VisualC+/常規(guī)/空項(xiàng)目”,并將如下代碼文件添加到該項(xiàng)目中:/Process.cpp#include<windows.h>#include<stdio.h>#include<tchar.h>void_tmain()STA
17、RTUPINFOsi;/定義啟動(dòng)信息結(jié)構(gòu)變量PROCESS_INFORMATIONpi;/定義進(jìn)程信息結(jié)構(gòu)變量將si清零ZeroMemory(&si,sizeof(si);/si.cb=sizeof(si);/用啟動(dòng)信息結(jié)構(gòu)的大小來設(shè)置si的cb字段ZeroMemory(&pi,sizeof(pi);/將pi清零/啟動(dòng)子進(jìn)程if(!CreateProcess("E:CDPlay.exe",模塊名(須換成你自己磁盤上的某個(gè)可執(zhí)行文件路徑)NULL,/無命令行(使用模塊名)NULL,/采用默認(rèn)的進(jìn)程安全屬性NULL,/采用默認(rèn)的線程安全屬性FALSE,/設(shè)置句柄
18、繼承為假0,/無創(chuàng)建標(biāo)志NULL,/使用父進(jìn)程的環(huán)境塊NULL,/使用父進(jìn)程的啟動(dòng)目錄&si,/指向STARTUPINFO結(jié)構(gòu)的指針&pi)/指向PROCESS_INFORMATION結(jié)構(gòu)的指針)(printf("CreateProcessfailed(%d)n",GetLastError();return;/等待直到子進(jìn)程退出WaitForSingleObject(pi.hProcess,INFINITE);/關(guān)閉進(jìn)程和線程句柄CloseHandle(pi.hProcess);CloseHandle(pi.hThread);5 .C執(zhí)行函數(shù)execl和除了
19、調(diào)用上面介紹的WindowsAPI函數(shù)外,還可以使用C語(yǔ)言運(yùn)行庫(kù)中的_wexecl等執(zhí)行函數(shù)來裝入和執(zhí)行新的子進(jìn)程(定義在頭文件<process.h>中):/普通字符串版intptr_t_execl(/成功不返回值,失敗返回-1constchar*cmdname,被執(zhí)行文件的路徑串constchar*arg0,.constchar*argn,/參數(shù)指針歹U表NULL);/寬字符串版intptr_t_wexecl(constwchar_t*cmdname,constwchar_t*arg0,constwchar_t*argn,NULL);22.2.2 創(chuàng)建、管理和終止線程MFC中提供
20、了線程類CWinThread,參見圖22-3。在MFC中區(qū)分兩種類型的線程:用戶界面線程(user-interfacethread)和輔助線程(workerthread)。用戶界面線程通常用于處理用戶輸入及響應(yīng)用戶生成的事件和消息。輔助線程通常用于完成CObjectApplicationArchitectureCCmdTargetCWinThreadCWinAppCOieControiModuieuserapplicatiori圖22-3CWinThread類及其派生類不需要用戶輸入的任務(wù)(如重新計(jì)算)。Win32API則不區(qū)分線程類型;它只需要了解線程的起始地址以開始執(zhí)行線程。MFC為用戶界
21、面中的事件提供消息泵(messagepump),從而對(duì)用戶界面線程進(jìn)行專門處理。CWinApp是用戶界面線程對(duì)象的一個(gè)示例,因?yàn)樗鼜腃WinThread派生并對(duì)用戶生成的事件和消息進(jìn)行處理。1 .創(chuàng)建線程MFC應(yīng)用程序中的所有線程都由CWinThread對(duì)象表示。大多數(shù)情況下,甚至不必顯式創(chuàng)建這些對(duì)象,而只需調(diào)用MFC框架的助手型全局函數(shù)AfxBeginThread,該函數(shù)將自動(dòng)創(chuàng)建CWinThread對(duì)象。也可以先創(chuàng)建自己的線程(C+)對(duì)象,再利用線程類的成員函數(shù)CreateThread來創(chuàng)建Windows線程。1)利用全局函數(shù)AfxBeginThread創(chuàng)建線程AfxBeginThread
22、函數(shù)有兩個(gè)版本,分別用于創(chuàng)建用戶界面線程和輔助線程:CWinThread*AfxBeginThread(/創(chuàng)建用戶界面線程CRuntimeClass*pThreadClass,intnPriority=THREAD_PRIORITY_NORMAL,UINTnStackSize=0,DWORDdwCreateFlags=0,LPSECURITY_ATTRIBUTESlpSecurityAttrs=NULL);CWinThread*AfxBeginThread(/創(chuàng)建輔助線程AFX_THREADPROCpfnThreadProc,LPVOIDpParam,intnPriority=THREAD_P
23、RIORITY_NORMAL,UINTnStackSize=0,DWORDdwCreateFlags=0,LPSECURITY_ATTRIBUTESIpSecurityAttrs=NULL);例如:classCMyThread:publicCWinThreadCMyThread*pMyThread=(CMyThread*)AfxBeginThread(RUNTIME_CLASS(CSockThread);if(pMyThread!=NULL)pMyThread->ResumeThread();及UINTWorkerThread(LPVOIDpParam)CWnd*pWin=(CWnd*)
24、pParam;AfxBeginThread(WorkerThread,this);2)利用CWinThread類的CreateThread函數(shù)創(chuàng)建線程可以先從CWinThread類派生自己的線程類,再利用CWinThread的成員函數(shù)CreateThread來創(chuàng)建線程:BOOLCreateThread(DWORDdwCreateFlags=0,UINTnStackSize=0,LPSECURITY_ATTRIBUTESlpSecurityAttrs=NULL);例如:classCMyThread:publicCWinThreadCMyThread*pMyThread=newCMyThread;
25、pMyThread->CreateThread();實(shí)際上,1)中的AfxBeginThread函數(shù)也是通過先創(chuàng)建一個(gè)CWinThread對(duì)象,再調(diào)用其CreateThread函數(shù)來完成線程創(chuàng)建的。2 .管理線程管理線程包括獲取線程對(duì)象、獲取和設(shè)置線程優(yōu)先級(jí)、檢查線程是否活動(dòng)、掛起和恢復(fù)線程等操作。1)獲取線程對(duì)象可以利用MFC的全局函數(shù)AfxGetThread來獲取當(dāng)前的線程對(duì)象:CWinThread*AfxGetThread();2)獲取和設(shè)置線程優(yōu)先級(jí)禾用CWinThread類的成員函數(shù)GetThreadPriority和SetThreadPriority可以獲取和設(shè)置線程的優(yōu)先級(jí)
26、:intGetThreadPriority();BOOLSetThreadPriority(intnPriority);線程的優(yōu)先級(jí)是在其所屬進(jìn)程優(yōu)先級(jí)的基礎(chǔ)上的一種相對(duì)優(yōu)先級(jí),nPriority的取值可為如下符號(hào)常量:表22-2線程優(yōu)先級(jí)符號(hào)常量?jī)?yōu)先級(jí)符號(hào)常量相對(duì)數(shù)值時(shí)間臨界THREAD_PRIORITY_TIME_CRITICAL15曷最高THREAD_PRIORITY_HIGHEST2T_y局普通THREAD_PRIORITY_ABOVE_NORMAL1|一一一|普通THREAD_PRIORITY_NORMAL0|彳氐普通THREADPRIORITYBELOWNORMAL-1J_低最低T
27、HREAD_PRIORITY_LOWEST-2空閑THREAD_PRIORITY_IDLE-153)檢查線程是否活動(dòng)可以利用API函數(shù)GetExitCodeThread返回的退出代碼是否為STILL_ACTIVE(259)來判斷指定線程是否仍然在運(yùn)行:BOOLGetExitCodeThread(HANDLEhThread,LPDWORDlpExitCode);例如:DWORDec;GetExitCodeThread(pMyThread->m_hThread,&ec);if(ec=STILL_ACTIVE),4)掛起和恢復(fù)線程可禾ij用CWinThread類的成員函數(shù)Suspend
28、Thread和ResumeThread來掛起和恢復(fù)線程的運(yùn)行:DWORDSuspendThread();DWORDResumeThread();3 .終止線程可以調(diào)用MFC的全局函數(shù)AfxEndThread來終止當(dāng)前線程:voidAFXAPIAfxEndThread(UINTnExitCode,BOOLbDelete=TRUE);為了正常終止一個(gè)輔助線程,還可以使用return語(yǔ)句;為了正常終止一個(gè)用戶界面線程,還可以在線程內(nèi)調(diào)用WindowsSDK中的PostQuitMessage函數(shù):voidPostQuitMessage(intnExitCode);還可以利用API函數(shù)Terminate
29、Thread強(qiáng)制終止指定線程:BOOLWINAPITerminateThread(HANDLEhThread,DWORDdwExitCode);CObjectApplicationArchitectureCCmdTargetSynchroniftiomCSyncObJectCCriticalSectionCEventCMutexCSemaphareSvnchronizationCMultiLockCSingleLock不過這樣做是危險(xiǎn)的。22.2.3 線程的同步由于同一進(jìn)程的多個(gè)線程共享同樣的數(shù)據(jù)段,具有同一地址空間。為了解決并行或并發(fā)的多個(gè)線程同時(shí)訪問同一數(shù)據(jù)或資源所造成的沖突,需要解決線程
30、的同步(synchronization)問題。Windows操作系統(tǒng)提供了多種同步對(duì)象,并可替我們管理圖22-3MFC同步類同步對(duì)象的加鎖和解鎖操作。我們的任務(wù)只是對(duì)每個(gè)需要同步使用的數(shù)據(jù)和資源產(chǎn)生一個(gè)同步對(duì)象,并在使用這些數(shù)據(jù)和資源前申請(qǐng)加鎖,且在使用完成后申請(qǐng)解鎖。Windows設(shè)置了4種同步對(duì)象臨界區(qū)(criticalsection)互斥量(mutex)、信號(hào)量(semaphore)和事件(event)。其中,除了臨界區(qū)外,其余三種同步對(duì)象都是操作系統(tǒng)的內(nèi)核對(duì)象。MFC封裝了這4種同步對(duì)象,對(duì)應(yīng)的類分別為CCriticalSection、CMutex、CSemaphore和CEvent,
31、它們都是同步對(duì)象類CSyncObject的派生類。另外,MFC還提供了兩個(gè)同步訪問對(duì)象類CMultiLock和CSingleLock,它們倆都是沒有基類的獨(dú)立類,參見表22-3和圖22-3。表22-3MFC中的同步類對(duì)象名類名描述只允許當(dāng)前進(jìn)程中的一個(gè)線程只有一個(gè)應(yīng)用程序使用此臨界區(qū)CCriticalSection訪問杲個(gè)對(duì)象資源時(shí)只允許系統(tǒng)中的一個(gè)進(jìn)程內(nèi)的口有多個(gè)應(yīng)用程序使用此互斥里CMutex一個(gè)線程訪問杲個(gè)對(duì)象資源時(shí)只允許知道數(shù)目的線程同時(shí)訪同一應(yīng)用程序內(nèi)多個(gè)線程CSemaphore何杲個(gè)對(duì)象可以同時(shí)訪問此資源時(shí)當(dāng)某個(gè)事件發(fā)生時(shí)通知一個(gè)程必須等到發(fā)生某事才能訪事件CEvent序(的線程)
32、問資源時(shí)為多個(gè)訪問對(duì)象加鎖在一特定時(shí)間需使用多個(gè)多鎖CMultiLock對(duì)象時(shí)單鎖CSingleLock為單個(gè)訪問對(duì)象加鎖一次等待一個(gè)對(duì)象時(shí)1 .臨界區(qū)臨界區(qū)(criticalsection)是一個(gè)進(jìn)程中的所有線程共享的某個(gè)受保護(hù)的資源或代碼段,被鎖定后每次只能被一個(gè)線程所使用。臨界區(qū)也是最容易使用的同步對(duì)象,但是只能用于單個(gè)進(jìn)程中的線程同步,而不能被其他進(jìn)程共享。由于臨界區(qū)對(duì)象不是Windows的內(nèi)核對(duì)象,所以它存在于進(jìn)程的內(nèi)存空間中。為了在MFC中使用臨界區(qū),必須先創(chuàng)建一個(gè)CCriticalSection對(duì)象;在線程進(jìn)入臨界區(qū)之前,調(diào)用該對(duì)象的成員函數(shù)Lock來鎖定臨界區(qū);在線程離開臨界區(qū)
33、之后,調(diào)用該對(duì)象的成員函數(shù)Unlock來解鎖臨界區(qū)。如果在調(diào)用Lock函數(shù)時(shí),沒有其他線程鎖定臨界區(qū),則Lock函數(shù)對(duì)臨界區(qū)加鎖后立即返回,使調(diào)用它的線程繼續(xù)運(yùn)行;如果在調(diào)用Lock函數(shù)時(shí),已經(jīng)有其他線程鎖定了臨界區(qū),則Lock函數(shù)被掛起,直到其他線程解鎖臨界區(qū)后才能返回。例子:創(chuàng)建臨界區(qū)對(duì)象CCriticalSectiong_cs;/intg_iNum=0;/需鎖定的全局變量(受保護(hù)的同步數(shù)據(jù)資源)自定義的線程過程處理函數(shù)DWORDThreadProc(LPVOIDpParam)/g_cs.Lock();/力口鎖g_iNum+;/臨界區(qū)(受保護(hù)的代碼段)g_cs.Unlock();/解鎖re
34、turn0;2 .互斥量互斥量(mutex)的用途與臨界區(qū)類似,但是它可以跨進(jìn)程使用,能用來同步多個(gè)進(jìn)程的數(shù)據(jù)共享訪問,不過互斥量的操作速度比臨界區(qū)的要慢近百倍。由于互斥量是Windows的內(nèi)核對(duì)象,所以它存在于系統(tǒng)內(nèi)存空間中,并具有引用計(jì)數(shù)。MFC的CMutex類封裝了互斥量對(duì)象,其使用方法類似于臨界區(qū)類的一一先構(gòu)造一個(gè)CMutex對(duì)象,再調(diào)用函數(shù)Lock來鎖定互斥量;在線程離開互斥量之后,調(diào)用Unlock來解鎖互斥量。下面是CMutex類的構(gòu)造函數(shù)及從其基類繼承下來的加鎖與解鎖函數(shù):CMutex(BOOLbInitiallyOwn=FALSE,/指定創(chuàng)建線程是否初始時(shí)有權(quán)訪問被保護(hù)的資源L
35、PCTSTRlpszName=NULL,/跨進(jìn)程使用時(shí)需指定相同的互斥量名LPSECURITY_ATTRIBUTESlpsaAttribute=NULL/指向安全屬性結(jié)構(gòu)的指針);virtualBOOLLock(DWORDdwTimeout=INFINITE);virtualBOOLUnlock()=0;其中,構(gòu)造函數(shù)中的bInitiallyOwn參數(shù)為TRUE時(shí),則會(huì)直到(指定名稱的)互斥量可用時(shí),才會(huì)從對(duì)此構(gòu)造函數(shù)的調(diào)用中返回,可用于在希望等待的時(shí)刻來創(chuàng)建一個(gè)CMutex對(duì)象。INFINITE)。加鎖函數(shù)中的dwTimeout參數(shù),用于指定等待時(shí)間的毫秒數(shù),缺省為無限長(zhǎng)(如果指定了該參數(shù)的
36、有限值,則超時(shí)時(shí)會(huì)放棄加鎖并返回FALSE。例子:CMutexg_mx;/創(chuàng)建互斥量對(duì)象intg_iNum=0;/需鎖定的全局變量(受保護(hù)的同步數(shù)據(jù)資源)DWORDThreadProc(LPVOIDpParam)/自定義的線程過程處理函數(shù)if(g_mx.Lock(100)/力口鎖g_iNum+;/互斥量(受保護(hù)的代碼段)g_mx.Unlock();/解鎖return0;3 .信號(hào)量信號(hào)量(semaphore,旗語(yǔ))也是一種內(nèi)核對(duì)象,用于資源的計(jì)數(shù)(可使用該資源的線程個(gè)數(shù))。當(dāng)線程用信號(hào)量對(duì)象的句柄作參數(shù)來調(diào)用等待函數(shù)WaitForSingleObject時(shí),系統(tǒng)會(huì)檢查該信號(hào)量所對(duì)應(yīng)的資源數(shù)是否
37、大于0(即資源是否可用),若大于0(稱為有信號(hào)signaled)則減少資源計(jì)數(shù)并喚醒線程,若等于0(稱為無信號(hào)nonsignaled)則讓線程進(jìn)入睡眠狀態(tài)直到(超出指定時(shí)間或當(dāng)指定時(shí)間為無限時(shí))占用該資源的其他線程釋放資源并增加資源的計(jì)數(shù)(使信號(hào)量大于0,即有信號(hào)或有資源可用)為止。信號(hào)量與前面講的臨界區(qū)和互斥量不同,它不屬于某個(gè)線程,而且它允許(不同進(jìn)程中的)多個(gè)線程同時(shí)訪問一個(gè)受保護(hù)的資源(資源的可訪問線程數(shù)由信號(hào)量的最大計(jì)數(shù)值決定)。信號(hào)量對(duì)象可用來限制對(duì)共享資源(如串口)進(jìn)行訪問的線程數(shù)目(如須w計(jì)算機(jī)的串口總數(shù))。MFC中的信號(hào)量類為CSemaphore,其構(gòu)造函數(shù)為:CSemaph
38、ore(LONGlInitialCount=1,/初始計(jì)數(shù)值,須R0且wlMaxCountLONGlMaxCount=1,/最大計(jì)數(shù)值,須nlInitialCountLPCTSTRpstrName=NULL,/信號(hào)量名串,用于多個(gè)進(jìn)程中的線程LPSECURITY_ATTRIBUTESIpsaAttributes=NULL/安全屬性,NULL表缺省);為了訪問用信號(hào)量計(jì)數(shù)的資源,還需要?jiǎng)?chuàng)建一個(gè)CSingleLock(或CMultiLock)對(duì)象,來鎖定和釋放受保護(hù)的資源。CSingleLock類的成員函數(shù)有:explicitCSingleLock(/構(gòu)造函數(shù)CSyncObject*pObject
39、,/同步對(duì)象(如CSemaphore對(duì)象),不能為NULLBOOLbInitialLock=FALSE/初始時(shí)是否鎖定資源);BOOLIsLocked();/判斷是否鎖定BOOLLock(DWORDdwTimeOut=INFINITE);/鎖定資源BOOLUnlock();/釋放資源BOOLUnlock(/釋放資源LONGlCount,/釋放的訪問計(jì)數(shù),須R0LPLONGlPrevCount=NULL/(返回)原計(jì)數(shù)的指針,NULL表不返回);例子:CSemaphorem_smph(5,5);WaitForSingleObject(m_smph.m_hObject,INFINITE);CSin
40、gleLocksingleLock(&m_smph);singleLock.Lock();/Attempttolockthesharedresourceif(singleLock.IsLocked()/Resourcehasbeenlocked/Usethesharedresource/Nowthatwearefinished,/unlocktheresourceforothers.singleLock.Unlock();4 .事件有時(shí)線程并不是要訪問某個(gè)受保護(hù)的數(shù)據(jù)或資源,而是需要等待某一事件(event)的發(fā)生,這在GUI編程中十分常見。事件對(duì)象用于一個(gè)線程通知另一線程某一事件的發(fā)
41、生(發(fā)信號(hào)表示某一操作已經(jīng)完成),它是同步對(duì)象中形式最簡(jiǎn)單的一種,而且其同步的機(jī)制也是最具有彈性的。事件是一種內(nèi)核對(duì)象,具有激發(fā)(有信號(hào)signaled)和非激發(fā)(無信號(hào)nonsignaled)兩種狀態(tài),狀態(tài)完全由程序來控制。有兩類事件對(duì)象手工的(manual)和自動(dòng)的(automatic)。手工事件對(duì)象會(huì)保持(由函數(shù)SetEvent或ResetEvent所設(shè)置的)狀態(tài)不變,直到調(diào)用其他函數(shù);而自動(dòng)事件對(duì)象則在(至少一個(gè))線程被釋放后會(huì)自動(dòng)返回?zé)o信號(hào)(不可用)狀態(tài)。MFC的CEvent類封裝了Windows的事件對(duì)象,其成員函數(shù)有:CEvent(/構(gòu)造函數(shù)BOOLbInitiallyOwn=F
42、ALSE,/TRUE:線程對(duì)單/多鎖對(duì)象可用;/FALSE:想訪問資源的所有線程都必須等待BOOLbManualReset=FALSE,/TRUE:手工事件對(duì)象;FALSE:自動(dòng)事件對(duì)象LPCTSTRlpszName=NULL,/事件名串,用于跨進(jìn)程的線程LPSECURITY_ATTRIBUTESlpsaAttribute=NULL/安全屬性,NULL表缺省);BOOLSetEvent();/設(shè)置事件為有信號(hào)(激發(fā)),釋放任意在等待的線程BOOLResetEvent();/設(shè)置事件為無信號(hào)(未激發(fā)),直到調(diào)用SetEvent才能激發(fā)BOOLPulseEvent();/先激發(fā)事件,在釋放等待的線
43、程后,再自動(dòng)非激發(fā)事件BOOLUnlock();/釋放事件對(duì)象使用CEvent對(duì)象的一般方法是,在適當(dāng)需要的時(shí)候構(gòu)造CEvent對(duì)象,在適當(dāng)?shù)臅r(shí)候調(diào)用其SetEvent函數(shù)激發(fā)事件(使事件對(duì)象處于有信號(hào)狀態(tài)),在完成對(duì)所控資源的訪問后,再調(diào)用其Unlock函數(shù)釋放事件對(duì)象。例子:UINT_cdeclMyThreadProc(LPVOIDlpParameter)CEvent*pEvent=(CEvent*)(lpParameter);VERIFY(pEvent!=NULL);/等待事件被激發(fā):WaitForSingleObject(pEvent->m_hObject,INFINITE);/
44、終止線程二AfxEndThread(0,FALSE);return0L;voidCEvent_Test()/創(chuàng)建傳遞給線程例程的CEvent對(duì)象CEvent*pEvent=newCEvent(FALSE,FALSE);/創(chuàng)建等待事件的線程CWinThread*pThread;pThread=:AfxBeginThread(&MyThreadProc,pEvent,0,0,CREATE_SUSPENDED,NULL);pThread->m_bAutoDelete=FALSE;pThread->ResumeThread();/通知線程進(jìn)行下一工作項(xiàng)pEvent->SetE
45、vent();/等待線程銷毀事件并返回:WaitForSingleObject(pThread->m_hThread,INFINITE);deletepThread;deletepEvent;WindowsAPI還提供了多個(gè)互鎖函數(shù)Interlocked*,用于多個(gè)線程對(duì)一個(gè)共享變量的同步,能絕對(duì)保證改變變量的線程獨(dú)占對(duì)該變量的訪問。WindowsAPI還提供了一組使線程阻塞自身執(zhí)行的等待函數(shù),包括等待單個(gè)對(duì)象的SignalObjectAndWait、WaitForSingleObject和WaitForSingleObjectEx;等待多個(gè)對(duì)象的WaitForMultipleObjec
46、ts、WaitForMultipleObjectsEx、MsgWaitForMultipleObjects和MsgWaitForMultipleObjectsEx;發(fā)出提示的MsgWaitForMultipleObjectsEx、SignalObjectAndWait、WaitForMultipleObjectsEx和WaitForSingleObjectEx;注冊(cè)登記的RegisterWaitForSingleObject和UnregisterWaitEx。由于篇幅的限制,這里就不詳細(xì)介紹了。22.3 .NET的進(jìn)程和線程編程本節(jié)簡(jiǎn)介.NET框架下使用C#進(jìn)行進(jìn)程和線程編程的基本方法。22.
47、3.1 進(jìn)程編程在.NET的框架類庫(kù)中,與進(jìn)程編程相關(guān)的類有Process(進(jìn)程)、ProcessStartInfo(進(jìn)程啟動(dòng)信息)、ProcessModule(進(jìn)程模塊)等,它們都位于System.Diagnostics命名空間中(程序集為在System.dll中的System)。Process和ProcessModule的基類都為(System.ComponentModel命名空間中的)Component,而ProcessStartInfo為一個(gè)獨(dú)立的類。1. Process類Process類(組件)提供對(duì)正在計(jì)算機(jī)上運(yùn)行的進(jìn)程的訪問,可用來啟動(dòng)、停止、控制和監(jiān)視應(yīng)用程序等任務(wù)。使用Pro
48、cess組件,可以獲取正在運(yùn)行的進(jìn)程的列表,也可以啟動(dòng)新的進(jìn)程。Process類的ProcessorAffinity屬性可用于獲取或設(shè)置一些處理器,此進(jìn)程中的線程可以按計(jì)劃在這些處理器上運(yùn)行。其屬性值為System.IntPtr類型的位掩碼,表示關(guān)聯(lián)進(jìn)程內(nèi)的線程可以在其上運(yùn)行的處理器。默認(rèn)值為2n-1,其中n是計(jì)算機(jī)上的處理器數(shù)。這一點(diǎn)可用于多核編程。2. ProcessStartInfo類可使用ProcessStartInfo類來更好地控制啟動(dòng)的進(jìn)程。至少必須以手動(dòng)方式或使用構(gòu)造函數(shù)來設(shè)置(應(yīng)用程序或文檔的)文件名屬性FileName。此處,將文檔定義為具有與其關(guān)聯(lián)的打開或默認(rèn)操作的任何文件
49、類型。使用操作系統(tǒng)提供的“文件夾選項(xiàng)”對(duì)話框,可以查看計(jì)算機(jī)中已注冊(cè)的文件類型及其相關(guān)應(yīng)用程序。單擊“高級(jí)”按鈕可打開一個(gè)對(duì)話框,其中顯示了是否存在與特定注冊(cè)文件類型相關(guān)聯(lián)的打開操作。另外,還可使用ProcessStartInfo類來設(shè)置定義要對(duì)該文件執(zhí)行的操作的其他屬性。可以為Verb屬性指定特定于FileName屬性的類型的值。例如,可以為文檔類型指定“print1另外,還可以指定Arguments屬性值,這些值將成為傳遞給文件的打開過程的命令行參數(shù)。Arguments屬性例如,如果在FileName屬性中指定一個(gè)文本編輯器應(yīng)用程序,則可以使用指定將用該編輯器打開的一個(gè)文本文件。在進(jìn)程啟動(dòng)
50、前,可更改任何ProcessStartInfo屬性的值。而啟動(dòng)進(jìn)程后,更改這些值是沒有效果的。3. ProcessModule類ProcessModule類表示加載到特定進(jìn)程中的.dll或.exe文件。每個(gè)進(jìn)程包含一個(gè)或多個(gè)模塊,可用該類來獲取進(jìn)程中模塊的信息。4. 例子ProcessmyProcess=newProcess();/獲取記事本進(jìn)程的啟動(dòng)信息ProcessStartInfomyProcessStartInfo=newProcessStartInfo("notepad.exe");/Assign將記事本的StartInfo賦值給myProcess對(duì)象的Start
51、InfomyProcess.StartInfo=myProcessStartInfo;/創(chuàng)建一個(gè)記事本myProcess.Start();System.Threading.Thread.Sleep(1000);ProcessModulemyProcessModule;/獲取與myProcess關(guān)聯(lián)的所有模塊ProcessModuleCollectionmyProcessModuleCollection=myProcess.Modules;Console.WriteLine("記事本關(guān)聯(lián)模塊的屬性:");/顯示每個(gè)模塊的屬性for(inti=0;i<myProcessM
52、oduleCollection.Count;i+)myProcessModule=myProcessModuleCollectioni;的基地址為:Console.WriteLine("模塊為"+myProcessModule.ModuleName);Console.WriteLine(myProcessModule.ModuleName+myProcessModule.BaseAddress);Console.WriteLine(myProcessModule.ModuleName+"的入口點(diǎn)地址為:+myProcessModule.EntryPointAddr
53、ess);Console.WriteLine(myProcessModule.ModuleName+"的文件名為:"+myProcessModule.FileName);/獲取與myProcess關(guān)聯(lián)的主模塊myProcessModule=myProcess.MainModule;/顯示主模塊的屬性Console.WriteLine("進(jìn)程的主模塊為:”+myProcessModule.ModuleName);Console.WriteLine("進(jìn)程主模塊的基地址為:"+myProcessModule.BaseAddress);Console
54、.WriteLine("進(jìn)程主模塊的入口點(diǎn)地址為:"+myProcessModule.EntryPointAddress);Console.WriteLine("進(jìn)程主模塊的的文件名為:”+myProcessModule.FileName);myProcess.CloseMainWindow();22.3.2 線程編程.NET框架類庫(kù)的(位于mscorlib.dll的程序集mscorlib中的)System.Threading命名空間中的Thread類(基類為CriticalFinalizerObject),用于創(chuàng)建并控制線程、設(shè)置其優(yōu)先級(jí)并獲取其狀態(tài)。一個(gè)進(jìn)程可
55、以創(chuàng)建一個(gè)或多個(gè)線程以執(zhí)行與該進(jìn)程關(guān)聯(lián)的部分程序代碼。使用ThreadStart委托或ParameterizedThreadStart委托指定由線程執(zhí)行的程序代碼。使用ParameterizedThreadStart委托可以將數(shù)據(jù)傳遞到線程過程。在線程存在期間,它總是處于由ThreadState定義的一個(gè)或多個(gè)狀態(tài)中。可以為線程請(qǐng)求由ThreadPriority定義的調(diào)度優(yōu)先級(jí),但不能保證操作系統(tǒng)會(huì)接受該優(yōu)先級(jí)。從Object類繼承的GetHashCode方法,提供托管線程的標(biāo)識(shí)。在線程的生存期內(nèi),無論獲取該值的應(yīng)用程序域如何,它都不會(huì)和任何來自其他線程的值沖突。需要的說明是,操作系統(tǒng)ThreadId和托管線程沒有固定關(guān)系,這是因?yàn)榉峭泄芩拗髂芸刂仆泄芘c非托管線程之間的關(guān)系。特別是,復(fù)雜的宿主可以使用CLRHostingAPI針對(duì)相同的操作系統(tǒng)線程調(diào)度很多托管線程,或者在不同的操作系統(tǒng)線程之間移動(dòng)托管線程。一旦啟動(dòng)線程,便不必保留對(duì)Thread對(duì)象的引用。線程將繼續(xù)執(zhí)行,直到該線程過程完成。例子(可創(chuàng)建一個(gè)名為ThreadTest的VisualC#控制臺(tái)應(yīng)用程序,灰色部分是自動(dòng)生成的。輸出如圖22-4所示):usingSystem;usingSystem.Collections.Generic;usingSystem.Lin
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 住宅認(rèn)購(gòu)定金合同范本
- 倉(cāng)儲(chǔ)保管填寫合同范本
- 2025年四川貨運(yùn)從業(yè)資格證考試的技巧
- 一房三賣買賣合同范本
- 停息掛賬律師委托合同范本
- 個(gè)人外匯貸款合同范本
- 助資合同范本
- 個(gè)人買房購(gòu)房合同范本
- 公司稅貸合同范本
- 個(gè)人店面整體裝修合同范本
- 2015 年全國(guó)高校俄語(yǔ)專業(yè)四級(jí)水平測(cè)試試卷
- T∕CCCMHPIE 1.3-2016 植物提取物 橙皮苷
- 土石壩設(shè)計(jì)畢業(yè)設(shè)計(jì)
- 一季責(zé)任制整體護(hù)理持續(xù)改進(jìn)實(shí)例
- 清華抬頭信紙
- 毫火針療法PPT課件
- 三年級(jí)部編版語(yǔ)文下冊(cè)第二單元日積月累
- 蝴蝶蘭溫室工廠化栽培管理技術(shù)
- 原發(fā)性肺癌手術(shù)臨床路徑(最全版)
- 最新工程招投標(biāo)實(shí)訓(xùn)課程標(biāo)準(zhǔn)教案
- 企業(yè)職工流動(dòng)登記表格模板(最新)
評(píng)論
0/150
提交評(píng)論