chapter_10-C++多線程_第1頁(yè)
chapter_10-C++多線程_第2頁(yè)
chapter_10-C++多線程_第3頁(yè)
chapter_10-C++多線程_第4頁(yè)
chapter_10-C++多線程_第5頁(yè)
已閱讀5頁(yè),還剩25頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

1、第九章 多線程入門9.1匈牙利編程命名規(guī)則第九章至第十一章的程序中,函數(shù)、數(shù)據(jù)變量、常量等的命名都與以前各章不同。這是因?yàn)閃indows編程采用了匈牙利編程命名規(guī)則。這是由曾在微軟工作過(guò)的匈牙利程序員Charles Simonyi發(fā)明,通過(guò)微軟傳播到全世界。它分為兩部分:前綴符號(hào)(代表數(shù)據(jù)基本類型)+ 對(duì)象描述。表3-1:匈牙利命名法使用的前綴符號(hào)前綴數(shù)據(jù)類型(基本類型)c字符by字節(jié)(無(wú)符號(hào)字符)n整數(shù)b布爾型wWORD(無(wú)符號(hào)字)l長(zhǎng)整數(shù)dwDWORD(無(wú)符號(hào)長(zhǎng)整數(shù))fn函數(shù)指針s串sz, str以0為結(jié)束符的字符串p指針pch字符指針lp32位的長(zhǎng)整數(shù)指針h句柄msg消息g_全局變量m_

2、類的數(shù)據(jù)成員s_靜態(tài)變量具體規(guī)則:(1) 變量:前綴 + 變量名。如:char * szName;BOOL bFlag;DWORD dwMAxCount;(2) 函數(shù):不帶前綴,名詞 + 動(dòng)詞。如:int ConvertNumber ( int x );void ShowMessage ( char * szMessage );(3) 常量和類型:全用大寫。如:#define MAX 256typedef unsigned char UCHAR;(4) 類(class): 前綴 + 變量名,使用大寫C作為前綴;其成員使用前綴“m_”。如:class CMyClasschar * m_szNam

3、e;public:CMyClass ();CMyClass ( );m_Display( );(5)前綴可復(fù)合使用。如上例中的m_szName,又如g_nCount代表整型的全局變量。本章將遵循此命名規(guī)則。9.2多任務(wù)操作對(duì)應(yīng)用程序開發(fā)者(課程中簡(jiǎn)稱他們?yōu)橛脩簦﹣?lái)說(shuō),有時(shí)希望能夠同時(shí)做幾件事,也即同時(shí)打開和運(yùn)行多個(gè)應(yīng)用程序(用戶程序)。例如,用戶可以在一個(gè)應(yīng)用程序中編輯文件;與另一個(gè)應(yīng)用程序交換信息(例如從鍵盤及鼠標(biāo)輸入數(shù)據(jù),或者輸出數(shù)據(jù)至打印機(jī));同時(shí)還在第三個(gè)應(yīng)用程序中自動(dòng)進(jìn)行計(jì)算。這就是多任務(wù)操作(multi-tasking)。多任務(wù)是一個(gè)操作系統(tǒng)可以同時(shí)運(yùn)行和處理多個(gè)程序的能力,從而更

4、有效地使用計(jì)算機(jī)。操作系統(tǒng)基本上使用一個(gè)硬件時(shí)鐘為同時(shí)運(yùn)行的每個(gè)進(jìn)程分配“時(shí)間片”。如果時(shí)間片足夠小,并且機(jī)器也沒(méi)有由于程序太多而超負(fù)荷,則在用戶看來(lái),所有的這些程序似乎在同時(shí)運(yùn)行著。在大型計(jì)算機(jī)上,多任務(wù)是必然的。這些大型機(jī)通常有幾十甚至幾百個(gè)終端和它連接,而每個(gè)終端用戶都應(yīng)該感覺(jué)到它獨(dú)占了整個(gè)計(jì)算機(jī)。另外,大型機(jī)的操作系統(tǒng)通常允許用戶“提交后臺(tái)作業(yè)”,這些后臺(tái)作業(yè)可以在用戶進(jìn)行其它工作時(shí),由機(jī)器動(dòng)態(tài)地“插空”完成。Windows是一種多任務(wù)的操作系統(tǒng),它具備多個(gè)進(jìn)程,每個(gè)進(jìn)程內(nèi)又可能包含一個(gè)或多個(gè)線程。Windows的32位版本W(wǎng)in32支持真正的多任務(wù),即在競(jìng)爭(zhēng)CPU時(shí)間的進(jìn)程、線程之間

5、分配CPU時(shí)間,從而出現(xiàn)多個(gè)進(jìn)程、線程同時(shí)運(yùn)行的現(xiàn)象。其中包括多線程操作。32位Windows操作系統(tǒng)提供了多種工作方式,在32位Windows環(huán)境下,Win32 API提供了多線程應(yīng)用程序開發(fā)所需要的接口函數(shù);利用VC+中提供的標(biāo)準(zhǔn)C庫(kù)也可開發(fā)多線程應(yīng)用程序;同時(shí)相應(yīng)的MFC類庫(kù)封裝了多線程編程的類,用戶在開發(fā)時(shí)可根據(jù)應(yīng)用程序的需要和特點(diǎn)選擇相應(yīng)的工具。此處將只介紹在Win32 API方式下如何編制多線程程序。Win32 API是32位Windows操作系統(tǒng)的應(yīng)用程序編程接口(API,Application Programming Interface)的簡(jiǎn)稱。其程序簡(jiǎn)稱為Win32程序?,F(xiàn)在

6、Windows操作系統(tǒng)下運(yùn)行的大多數(shù)應(yīng)用程序都是Win32應(yīng)用程序。在操作系統(tǒng)的教材中,都提到過(guò)程序和進(jìn)程的概念:程序是一段代碼,它是一個(gè)靜態(tài)的概念,進(jìn)程(process)是程序的一次執(zhí)行,它是一個(gè)動(dòng)態(tài)的概念。進(jìn)程是當(dāng)前操作系統(tǒng)下的一個(gè)被加載到內(nèi)存的、正在運(yùn)行的應(yīng)用程序的實(shí)例。每個(gè)進(jìn)程都是由內(nèi)核對(duì)象和地址空間所組成的,內(nèi)核對(duì)象可以讓系統(tǒng)在其內(nèi)存放有關(guān)進(jìn)程的統(tǒng)計(jì)信息,并且使系統(tǒng)能夠以此來(lái)管理進(jìn)程,而地址空間則包括了所有程序模塊的代碼和數(shù)據(jù),以及線程堆棧、堆區(qū)等動(dòng)態(tài)分配的空間。進(jìn)程具有生命周期,在其生命周期期間,具有不同的狀態(tài):例如新建、運(yùn)行、阻塞、就緒和完成5個(gè)狀態(tài)中的一種。在多任務(wù)操作系統(tǒng)中,

7、可同時(shí)運(yùn)行多個(gè)進(jìn)程,每個(gè)進(jìn)程都有自己的虛擬地址空間。進(jìn)程在自己的地址空間中修改數(shù)據(jù)而不會(huì)影響其它進(jìn)程。操作系統(tǒng)為了識(shí)別不同的進(jìn)程,為每個(gè)進(jìn)程分配一個(gè)進(jìn)程標(biāo)識(shí)。進(jìn)程和線程進(jìn)程僅僅是一個(gè)存在,不能獨(dú)自完成任何操作,它必須擁有至少一個(gè)在其環(huán)境下運(yùn)行的線程。在進(jìn)程啟動(dòng)的同時(shí)即同時(shí)為它啟動(dòng)了一個(gè)線程,該線程稱作主線程或基本線程(primary thread)或執(zhí)行線程(thread of execution),此主線程負(fù)責(zé)執(zhí)行在進(jìn)程地址空間內(nèi)的代碼。此外,這個(gè)主線程也可繼續(xù)創(chuàng)建子線程。如果主線程退出,則進(jìn)程也就沒(méi)有存在的可能,系統(tǒng)將自動(dòng)撤銷該進(jìn)程并且釋放其地址空間。因此,如果在單進(jìn)程環(huán)境中,有兩個(gè)或多個(gè)

8、線程正在運(yùn)行,則這些線程將共享單個(gè)地址空間。這些線程能執(zhí)行相同的代碼,對(duì)相同的數(shù)據(jù)進(jìn)行操作。這些線程還能共享內(nèi)核對(duì)象句柄,因?yàn)榫浔砀褚蕾囉诿總€(gè)進(jìn)程而不是每個(gè)線程。多線程是在一個(gè)程序內(nèi)部實(shí)現(xiàn)多任務(wù)的能力。進(jìn)程可以把它自己分割為單獨(dú)的執(zhí)行“線程”,這些線程似乎同時(shí)在運(yùn)行。它能讓進(jìn)程使用多線程在后臺(tái)執(zhí)行長(zhǎng)作業(yè),而同時(shí)又進(jìn)行其它工作。多線程處理提供的重要好處是在程序的組織方面,可以使程序的設(shè)計(jì)極大的簡(jiǎn)化。CPU將會(huì)輪流給每個(gè)線程分配一些CPU時(shí)間。每個(gè)線程都覺(jué)得自己在一直占有CPU,但事實(shí)上CPU時(shí)間被切成片段分配給所有的線程。線程處理多少會(huì)降低進(jìn)行計(jì)算的效率,但是從改善程序設(shè)計(jì)、資源平衡以及給用戶

9、提供方便等方面來(lái)說(shuō),還是相當(dāng)值得的。一般情況下,使用線程能創(chuàng)建一個(gè)更加松散耦合的設(shè)計(jì)(loosely coupled design)。線程是進(jìn)程的一條執(zhí)行路徑,是同一進(jìn)程的不同部分,它包含獨(dú)立的堆棧和CPU寄存器狀態(tài),所有線程共享所有的進(jìn)程資源,包括打開的文件、全局定義的變量、靜態(tài)變量、信號(hào)標(biāo)識(shí)及動(dòng)態(tài)分配的內(nèi)存等。一個(gè)進(jìn)程中的所有線程使用相同地址空間,而這些線程的執(zhí)行由系統(tǒng)調(diào)度程序控制,調(diào)度程序決定哪個(gè)線程可運(yùn)行及何時(shí)運(yùn)行。每個(gè)線程還有它自己的處理器狀態(tài)(和數(shù)學(xué)協(xié)處理器狀態(tài)),這個(gè)狀態(tài)在進(jìn)行線程切換期間被保存和恢復(fù)。線程有優(yōu)先級(jí)別,優(yōu)先權(quán)較低的線程必須等到優(yōu)先權(quán)較高的線程執(zhí)行完后再執(zhí)行。一條線

10、程可控制另一條線程的運(yùn)行。使用多線程處理(multithreading),一個(gè)進(jìn)程可以有多個(gè)并發(fā)執(zhí)行的線程。每個(gè)獨(dú)立的子任務(wù)都由運(yùn)行的線程(thread of execution)驅(qū)動(dòng),程序就好像每個(gè)線程都擁有自己的CPU。盡管在進(jìn)程的整個(gè)生存周期內(nèi)許多進(jìn)程都始終只有一個(gè)主線程在運(yùn)行,但進(jìn)程在整個(gè)生存周期內(nèi)擁有許多線程的情況并不少見(jiàn)。進(jìn)程GUI主線程重編頁(yè)碼子線程讀文件子線程打印子線程請(qǐng)注意,在上圖中,所有線程是在交叉地工作的,一個(gè)時(shí)間片內(nèi)只有一個(gè)線程在運(yùn)行。不同線程只是在分時(shí)地運(yùn)行。9.3線程的創(chuàng)建和終止當(dāng)一個(gè)Windows應(yīng)用程序運(yùn)行時(shí),它會(huì)自動(dòng)產(chǎn)生一個(gè)主線程,一般的窗口處理等都由該主線程

11、處理,在主線程中可以創(chuàng)建和使用其它子線程。子線程通常用于處理用戶輸入¤ 輸出操作并且響應(yīng)各種事件和消息。子線程可用三種方法創(chuàng)建:· 調(diào)用Win32 API函數(shù)CreateThread。· 調(diào)用C運(yùn)行庫(kù)函數(shù) _beginthread。· 調(diào)用C運(yùn)行庫(kù)函數(shù) _beginthreadex。此處只介紹第一種方法。創(chuàng)建新的執(zhí)行線程的API函數(shù)是CreateThread,請(qǐng)看下例:例1主線程與子線程共同運(yùn)行/ wnd_thread_5.cpp/ modification of program on p.410 of "Visual C+代碼參考與技巧大全&

12、quot;,郭克新編著/ Study of arguments of function CreateThread#include <windows.h>#include <iostream.h>void ThreadFunc (int *lpParam);void main( )int j = 3;int*lpParameter = &j;DWORD ThreadId;cout<<"This is primary thread!n"CreateThread(NULL, /no security attributes0, /defa

13、ult stack size(LPTHREAD_START_ROUTINE)ThreadFunc,/Thread functionlpParameter,/argument to thread function (pointer)NULL,/default creation flags&ThreadId);/returns thread IDSleep(1);/Wait for thread creation and executioncout<<"The ID of secondary thread is "<<ThreadId<&l

14、t;endl;void ThreadFunc(int *lpParam)for (int i=0; i<*lpParam; i+)cout<<i+1<<" round of ThreadFunc!"<<endl;/* Results:(A)This is primary thread!1 round of ThreadFunc!2 round of ThreadFunc!3 round of ThreadFunc!The ID of secondary thread is 3184(B) If "sleep" s

15、tatement is deleted, then the result may be:This is primary thread!The ID of secondary thread is 3212*/以上程序中的CreateThread函數(shù)原型為:HANDLE WINAPI CreateThread( LPSECURITY_ATTRIBUTES lpThreadAttributes, / security attributes DWORD dwStackSize, / stack size LPTHREAD_START_ROUTINE lpStartAddress, / pointer

16、to thread function LPVOID lpParameter, /argument to thread function DWORD dwCreationFlags, / creation flags LPDWORD lpThreadId);/returns thread ID其中六個(gè)參數(shù)分別介紹如下:第一個(gè)參數(shù)是指向SECURITY_ATTRIBUTES類型的結(jié)構(gòu)體的指針。一般不用,可設(shè)置為NULL(或0)。第二個(gè)參數(shù)是新線程(子線程)的初始堆棧大小,此處設(shè)置為NULL,使用默認(rèn)值。第三個(gè)參數(shù)是指向子線程函數(shù)的指針。函數(shù)名稱可以是任意的,但子線程函數(shù)必須有如下函數(shù)原型:DWOR

17、D WINAPI ThreadProc (PVOID pParam);第四個(gè)參數(shù)是傳遞給ThreadProc的參數(shù),它是一個(gè)指向整型變量的指針。這是主線程和子線程共享的數(shù)據(jù)。第五個(gè)參數(shù)通常為0,表示直接執(zhí)行子線程。如果不希望創(chuàng)建的線程馬上執(zhí)行,則應(yīng)標(biāo)志為CREATESUSPENDED。線程將掛起直到調(diào)用ResumeThread函數(shù)。第六個(gè)參數(shù)是一個(gè)指針,指向線程ID的值。以上程序中“sleep”語(yǔ)句的作用是等待子線程被創(chuàng)建并執(zhí)行。這是程序運(yùn)行結(jié)果的Result(A)。如果刪去“sleep”語(yǔ)句,則子線程來(lái)不及運(yùn)行完畢主線程就結(jié)束,整個(gè)進(jìn)程就結(jié)束。這是Result(B)。多線程程序中必須包含頭文

18、件<windows.h>,因它包含了所有線程處理函數(shù)的函數(shù)原型。程序中一些常量和數(shù)據(jù)類型的定義可參閱第十章§10.1.3“頭文件和系統(tǒng)生成的各類文件”。9.4線程間的同步及事件對(duì)象如果多個(gè)線程企圖讀取同一個(gè)公共變量,則不會(huì)出現(xiàn)問(wèn)題。然而,如果其中一個(gè)線程企圖修改這一個(gè)公共變量,則可能出現(xiàn)問(wèn)題。此時(shí),一個(gè)線程可能更新一個(gè)或多個(gè)變量,而另一個(gè)線程可能讀取使用這些變量。當(dāng)線程共享幾個(gè)變量或數(shù)據(jù)結(jié)構(gòu)體時(shí),這么多個(gè)變量或結(jié)構(gòu)體的字段在它們之間必須是一致的。操作系統(tǒng)可能在更新這些變量的過(guò)程中間中斷一個(gè)線程,以致使用這些變量的線程得到的將是不一致的數(shù)據(jù)。其結(jié)果將產(chǎn)生矛盾,并且這樣的錯(cuò)誤

19、可能對(duì)程序造成破壞。因此當(dāng)進(jìn)程中有多個(gè)線程同時(shí)運(yùn)行時(shí),需要對(duì)線程的工作進(jìn)行協(xié)調(diào),使其同步運(yùn)行,以免出現(xiàn)沖突和競(jìng)爭(zhēng),甚至死鎖現(xiàn)象。為協(xié)調(diào)兩條或多條線程的執(zhí)行,可使用同步對(duì)象。它是一個(gè)數(shù)據(jù)結(jié)構(gòu),其當(dāng)前狀態(tài)可為信號(hào)態(tài)或非信號(hào)態(tài)。線程可與這些同步對(duì)象之一交互作用,或者等待同步對(duì)象變?yōu)樾盘?hào)態(tài)。當(dāng)線程等待一個(gè)對(duì)象時(shí),只要同步對(duì)象狀態(tài)是非信號(hào)態(tài),它的運(yùn)行就被阻塞。通常,線程在使用共享資源或執(zhí)行一種必須與其它線程協(xié)調(diào)的操作之前,將等待一個(gè)同步對(duì)象。同步對(duì)象有四種類型:事件(Event)對(duì)象、互斥(Mutex)對(duì)象、信號(hào)量(Semaphore)對(duì)象和臨界區(qū)(Critical Section)對(duì)象。每種類型的不同

20、特征決定了不同功能。本章主要介紹事件對(duì)象,還簡(jiǎn)單介紹互斥對(duì)象。9.4.1同步對(duì)象之一 - 事件事件是同步對(duì)象,它由操作系統(tǒng)管理。它允許進(jìn)程(線程)在某種情況發(fā)生時(shí),喚醒另外一條進(jìn)程(線程)。事件對(duì)象提供了通知一條或多條線程某事件已經(jīng)發(fā)生的信號(hào)機(jī)制。事件對(duì)象可被置位(to be set)而成為信號(hào)態(tài),或被復(fù)位(to be reset)而成為非信號(hào)態(tài)。事件對(duì)象也屬于內(nèi)核對(duì)象,它包含一個(gè)計(jì)數(shù)器,一個(gè)用于指明該事件是自動(dòng)復(fù)位事件還是手工復(fù)位事件的布爾值標(biāo)志,另一個(gè)用于指明該事件處于已通知狀態(tài)抑或未通知狀態(tài)的布爾值標(biāo)志。當(dāng)線程必須等待另一線程完成任務(wù)時(shí),或者當(dāng)一個(gè)線程必須睡眠并且等待另一線程時(shí),可以使用

21、事件對(duì)象來(lái)表示某一事件已經(jīng)發(fā)生。有兩種不同類型的事件對(duì)象:一種是自動(dòng)復(fù)位型,另一種是手工復(fù)位型:(一)自動(dòng)復(fù)位型:當(dāng)一個(gè)自動(dòng)復(fù)位事件被置為信號(hào)態(tài)時(shí),等待該事件的線程中只有一個(gè)線程變?yōu)榭烧{(diào)度線程,也即只釋放一條等待線程,然后事件被自動(dòng)復(fù)位為非信號(hào)態(tài)。這種類型可用于釋放幾條等待線程之一。自動(dòng)復(fù)位事件也可由一條監(jiān)視線程使用,通知某感興趣事件的發(fā)生。(二)手工復(fù)位型:當(dāng)一個(gè)手工復(fù)位事件被設(shè)為信號(hào)態(tài)時(shí),等待該事件的所有線程均變?yōu)榭蛇\(yùn)行線程,也即所有等待線程都將釋放,直到事件被顯式地置為非信號(hào)態(tài)。一個(gè)手工復(fù)位事件可供一個(gè)有幾條線程的進(jìn)程使用,例如其中一條線程向共享內(nèi)存的一個(gè)區(qū)域里寫,而其他線程從中讀取,寫

22、者線程可以在寫的時(shí)候?qū)⑹录?fù)位到非信號(hào)態(tài),臨時(shí)阻塞讀者線程。寫完后再將事件置位為信號(hào)態(tài),以允許所有其它線程自由地讀取,直到下一個(gè)寫操作發(fā)生時(shí)為止。每一事件都可以有一個(gè)相關(guān)聯(lián)的名稱,這通過(guò)句柄(handle)來(lái)表示。同一個(gè)事件句柄可由多個(gè)進(jìn)程或多條線程共享。所謂句柄是一個(gè)32位整型變量,它代表一個(gè)對(duì)象。如同使用指針一樣,Windows會(huì)自動(dòng)把一個(gè)句柄與一個(gè)對(duì)象關(guān)聯(lián)起來(lái)。許多API (Application Programming Interface) 函數(shù)返回句柄或者接受句柄作為參數(shù)。后面程序中的hInstance就是一個(gè)與應(yīng)用程序?qū)ο笙嚓P(guān)聯(lián)的句柄,它實(shí)際上是一個(gè)用于記錄對(duì)象位置的基本內(nèi)存地址。

23、注意,當(dāng)進(jìn)程結(jié)束時(shí),打開的句柄自動(dòng)被關(guān)閉。9.4.2事件對(duì)象的創(chuàng)建和置位一般使用CreateEvent函數(shù)來(lái)創(chuàng)建事件對(duì)象和獲取事件句柄。使用以下語(yǔ)句:HANDLE hEvent = CreateEvent (NULL, FALSE, FALSE, “EventName”);先聲明一個(gè)事件句柄hEvent,接著調(diào)用函數(shù),用于建立一個(gè)事件對(duì)象,同時(shí)返回所建立事件的句柄值,將其存于變量hEvent中。CreateEvent函數(shù)原型為:HANDLE WINAPI CreateEvent ( LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualRes

24、et, BOOL bInitialState, LPCSTR lpName );此函數(shù)具有四個(gè)形參,并且返回一個(gè)句柄。它的調(diào)用約定屬于WINAPI類型,也即_stdcall。這可由頭文件windef.h中以下語(yǔ)句看出:#define WINAPI _stdcall第二章§2.8.6“函數(shù)的調(diào)用約定” 提到,_stdcall是Pascal程序的默認(rèn)調(diào)用方式,是Win API函數(shù)使用的調(diào)用約定。參數(shù)從右向左依次傳遞并且壓入堆棧,函數(shù)調(diào)用結(jié)束后,由被調(diào)用函數(shù)負(fù)責(zé)堆棧的清理工作。下面分析CreateEvent函數(shù)的四個(gè)形參:· 指向SECURITY_ATTRIBUTES結(jié)構(gòu)體的指針

25、。一般可將此參數(shù)忽略,將其設(shè)置為NULL。· 標(biāo)志:表明這是否是手工復(fù)位(RESET)的事件。TRUE表示它是手工復(fù)位事件對(duì)象;FALSE表示它是自動(dòng)復(fù)位事件對(duì)象。本章所用程序例子都是自動(dòng)復(fù)位事件對(duì)象。· 標(biāo)志:表明事件的初始狀態(tài)。如果此參數(shù)為TRUE,則事件處于發(fā)信號(hào)狀態(tài);如果此參數(shù)為FALSE,則事件處于未發(fā)信號(hào)狀態(tài)。· 事件的可選名稱:這是一個(gè)以0為結(jié)束符的字符串。大多數(shù)情況下,當(dāng)用戶沒(méi)有在進(jìn)程間共享事件對(duì)象時(shí),該值設(shè)置為NULL。如果事件創(chuàng)建成功,則該函數(shù)返回事件句柄;如果事件創(chuàng)建失敗,則該函數(shù)返回NULL值。線程結(jié)束后,該事件對(duì)象自動(dòng)消失。也可在程序中刪

26、除事件句柄,調(diào)用以下函數(shù):CloseHandle (hEvent);事件一旦創(chuàng)建,它就可能處于以下兩種狀態(tài)之一:· 發(fā)信號(hào):對(duì)該事件的等待請(qǐng)求將被滿足。· 未發(fā)信號(hào):對(duì)該事件的等待請(qǐng)求不能滿足,等待線程將被阻塞。一般情況下,建立后的事件對(duì)象處于未發(fā)信號(hào)狀態(tài)。因此創(chuàng)建事件對(duì)象后,尚須修改其狀態(tài),將其改為發(fā)信號(hào)狀態(tài),如下:SetEvent (hEvent);此函數(shù)只有一個(gè)參數(shù),就是事件句柄。一般創(chuàng)建的事件對(duì)象屬于自動(dòng)復(fù)位事件。在等待該事件的線程被釋放并且開始工作后,事件即自動(dòng)被復(fù)位。而對(duì)于手工復(fù)位事件,可以使用以下函數(shù)將其復(fù)位:ResetEvent (hEvent);9.4.3

27、 等待單個(gè)事件對(duì)象發(fā)信號(hào)在線程等待同步對(duì)象句柄發(fā)信號(hào)時(shí),可以使用下面兩個(gè)函數(shù)來(lái)把線程置為等待狀態(tài):· WaitForSingleObject:當(dāng)線程等待一個(gè)句柄發(fā)信號(hào)時(shí)可以用此函數(shù)。· WaitForMultipleObjects:當(dāng)線程等待一個(gè)句柄數(shù)組(包括多個(gè)句柄)發(fā)信號(hào)時(shí)可以用此函數(shù)。這兩個(gè)通用函數(shù)被線程用來(lái)等待一個(gè)或多個(gè)同步對(duì)象變?yōu)樾盘?hào)態(tài)。這兩個(gè)函數(shù)除了可用于等待事件、互斥和信號(hào)量這三類同步對(duì)象外,還可用于等待進(jìn)程和線程對(duì)象。但它們不能用于等待臨界區(qū)對(duì)象。先分析第一個(gè)函數(shù)WaitForSingleObject。它的函數(shù)原型如下:DWORD WINAPI WaitFo

28、rSingleObject( HANDLE hHandle, DWORD dwMilliseconds );WaitForSingleObject測(cè)試做為返回參數(shù)被傳送給它的對(duì)象句柄(DWORD數(shù)據(jù)類型),如果測(cè)得句柄為零值,表示句柄已發(fā)信號(hào),等待線程可繼續(xù)運(yùn)行。否則該函數(shù)被阻塞,須繼續(xù)等待并且不斷測(cè)試。見(jiàn)下例:DWORD dwResult = WaitForSingleObject (hEvent, INFINITE);WaitForSingleObject有兩個(gè)參數(shù):· hHandle:需要測(cè)試的同步對(duì)象的句柄。· dwMilliseconds:以毫秒為單位的一個(gè)時(shí)限值

29、,如果沒(méi)有時(shí)間限制則為INFINITE(或?yàn)?1)。這由預(yù)定義語(yǔ)句所規(guī)定:#define INFINITE 0xFFFFFFFF / Infinite timeoutWaitForSingleObject函數(shù)等待這些對(duì)象類型的單個(gè)實(shí)例(例如單個(gè)事件)。下面是WaitForSingleObject可能返回的兩個(gè)結(jié)果:· WAIT_OBJECT_0(=0):當(dāng)事件是發(fā)信號(hào)狀態(tài)或在時(shí)限之內(nèi)轉(zhuǎn)變?yōu)樾盘?hào)態(tài),返回該值。注意該符號(hào)的最后一個(gè)字符是零,不是字母O。如果對(duì)象的狀態(tài)變?yōu)樾盘?hào)態(tài),則函數(shù)返回0,等待的進(jìn)程或線程可繼續(xù)運(yùn)行。· WAIT_TIMEOUT(=0x00000102):當(dāng)事

30、件未發(fā)信號(hào)而時(shí)限已到則返回該值。如果在對(duì)象變?yōu)樾盘?hào)態(tài)之前時(shí)限已過(guò),則函數(shù)返回非零值,等待的進(jìn)程或線程將被阻塞,必須繼續(xù)等待,直到其它線程改變了此對(duì)象的狀態(tài)為止。在大多數(shù)情況下,WaitForSingleObject還修改對(duì)象,將自動(dòng)復(fù)位事件對(duì)象復(fù)位為非信號(hào)態(tài)。但它不改變手工復(fù)位事件對(duì)象的狀態(tài)。如果時(shí)限dwMilliseconds設(shè)為INFINITE或者-1,函數(shù)將無(wú)限期地等待。對(duì)手工重置事件句柄而言,甚至在第一個(gè)線程的等待請(qǐng)求被滿足后,事件仍然保持發(fā)信號(hào)狀態(tài)。下圖是兩個(gè)線程間的同步關(guān)系。事件對(duì)象hEvent處于非信號(hào)態(tài):子線程A子線程BWaitForSingleEvent (hEvent);子

31、線程A被阻塞子線程A恢復(fù)運(yùn)行SetEvent (hEvent);一般來(lái)說(shuō),事件可用于表明特定的任務(wù)已經(jīng)完成。在該事件發(fā)信號(hào)前,等待這個(gè)任務(wù)完成的線程將一直等待,待該事件發(fā)信號(hào)后,線程然后繼續(xù)運(yùn)行,如上圖所示?,F(xiàn)在看兩個(gè)例子:例1兩個(gè)子線程共同交叉地賣票。/ wnd_thread_event_1.cpp/ modification of program on pp.415 of "Visual C+代碼參考與技巧大全",郭克新編著#include <windows.h>#include <iostream.h>void Fun1Proc ( void

32、);void Fun2Proc ( void );#define TOTAL 10int total = TOTAL;HANDLE g_hEvent;int count1=0;/ No. of tickets sold by thread 1int count2=0;/ No. of tickets sold by thread 2 void main( )g_hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);if ( !g_hEvent )cout<<"Event creation error!"<<e

33、ndl;SetEvent (g_hEvent);CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE)Fun1Proc, NULL, 0, NULL);CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE)Fun2Proc, NULL, 0, NULL);Sleep (500);/ Wait for threads to finish workingcout<<"First thread has sold "<<count1<<" tickets!&

34、quot;<<endl;cout<<"Second thread has sold "<<count2<<" tickets!"<<endl;void Fun1Proc ()while (TRUE)WaitForSingleObject (g_hEvent, INFINITE);if (total>0)count1+;cout<<"First thread sells ticket No. "<<total-<<endl;elsebr

35、eak; /from while loopSetEvent (g_hEvent);void Fun2Proc ( )while (TRUE)WaitForSingleObject (g_hEvent, INFINITE);if (total>0)count2+;cout<<"Second thread sells ticket No. "<<total-<<endl;elsebreak; /from while loopSetEvent (g_hEvent);/* Results例如:Second thread sells tick

36、et No. 10Second thread sells ticket No. 9First thread sells ticket No. 8Second thread sells ticket No. 7First thread sells ticket No. 6Second thread sells ticket No. 5First thread sells ticket No. 4Second thread sells ticket No. 3First thread sells ticket No. 2Second thread sells ticket No. 1First t

37、hread has sold 4 tickets!Second thread has sold 6 tickets!*/* 如不用WaitForSingleObject語(yǔ)句,則將出現(xiàn)線程競(jìng)爭(zhēng)的混亂現(xiàn)象,例如:Second tFirsa dhrelal sel s ticietcNok et No. 23Second tFirsa dhrelal sel s ticietcNok et No. 23First thread sells ticket No. 1First thread has sold 5 tickets!First thread has sold 5 tickets!Secon

38、d thread has sold 5 tickets!Second thread has sold 5 tickets!*/以上程序中,主線程創(chuàng)建兩條子線程,其任務(wù)是共同出賣TOTAL張票。子線程創(chuàng)建后,即進(jìn)入無(wú)限循環(huán),等待同步對(duì)象g_hEvent發(fā)信號(hào)。g_hEvent是事件對(duì)象,負(fù)責(zé)子線程之間的同步操作。主線程創(chuàng)建該事件對(duì)象,同時(shí)指定它是自動(dòng)復(fù)位事件,并在創(chuàng)建事件對(duì)象后接著將其置位。此時(shí)兩條等待子線程中有一條子線程的等待請(qǐng)求可被滿足,就可解除阻塞,繼續(xù)運(yùn)行并且賣票。同時(shí)事件對(duì)象立即自動(dòng)復(fù)位,要求該子線程賣完票后重新將事件對(duì)象置位,以供下一個(gè)子線程工作。以上程序中,主線程中三條語(yǔ)句g_hE

39、vent = CreateEvent (NULL, FALSE, FALSE, NULL);if ( !g_hEvent )cout<<"Event creation error!"<<endl;SetEvent (g_hEvent);可簡(jiǎn)化為以下兩條:g_hEvent = CreateEvent (NULL, FALSE, TRUE, NULL);if ( !g_hEvent )cout<<"Event creation error!"<<endl;從運(yùn)行結(jié)果看,有時(shí)兩條子線程的工作比較零亂,工作負(fù)擔(dān)不平

40、衡,有輕有重。如果它們能夠按順序工作,則工作負(fù)擔(dān)將會(huì)平衡。這可依靠多個(gè)事件對(duì)象。看下例。例2三個(gè)子線程按序工作:一個(gè)計(jì)時(shí);一個(gè)采集數(shù)據(jù);一個(gè)顯示數(shù)據(jù)。而主線程則完成計(jì)算、數(shù)據(jù)處理等后臺(tái)(Background work)工作。/ wnd_thread_event_4.cpp/ Multi-threading of a monitoring system/ main thread does background work/ 1st secondary-thread does timing/ 2nd secondary-thread does acquisition/ 3rd secondary-t

41、hread does displaying#include <windows.h>#include <iostream.h>#include <time.h>#include <conio.h>void ThreadTimer (void );void ThreadAquisition (void );void ThreadShow (void );int j = 0;/counterdouble temp_base = 23.5;/base value of temperature in Celsiusint pres_base = 982;/

42、base value of pressure in mmHgint hum_base = 46;/base value of humidity in %double temp = temp_base;/temperatureint pres = pres_base;/pressureint hum = hum_base;/humidityHANDLE g_hEvent_Timer;HANDLE g_hEvent_Acq;HANDLE g_hEvent_Show;void main( )CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE)ThreadTi

43、mer, NULL, 0, NULL);CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE)ThreadAquisition, NULL, 0, NULL);CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE)ThreadShow, NULL, 0, NULL);g_hEvent_Timer = CreateEvent (NULL, FALSE, FALSE, NULL);g_hEvent_Acq = CreateEvent (NULL, FALSE, FALSE, NULL);g_hEvent_Show =

44、CreateEvent (NULL, FALSE, FALSE, NULL);if ( (!g_hEvent_Timer) | (!g_hEvent_Acq) | (!g_hEvent_Show) ) cout<<"Error in creating events"<<endl; SetEvent (g_hEvent_Timer);for (int i=0; i<15; i+)cout<<"后臺(tái)工作!"Sleep (500);/執(zhí)行計(jì)算等后臺(tái)工作getch( );void ThreadTimer ()WaitFo

45、rSingleObject (g_hEvent_Timer, INFINITE);while (TRUE)SetEvent (g_hEvent_Acq);Sleep (1000); /等待一秒鐘void ThreadAquisition ( ) /讀取三個(gè)變量值while (TRUE)WaitForSingleObject (g_hEvent_Acq, INFINITE);SetEvent (g_hEvent_Show);Sleep (1);/ To avoid interference to ThreadAquisitionj = (j+)%3;temp = temp_base+j*1.5;

46、pres = pres_base+j*4;hum = hum_base+j*2;void ThreadShow () /顯示當(dāng)前時(shí)間和三個(gè)變量 long t;while (TRUE)WaitForSingleObject (g_hEvent_Show, INFINITE);time(&t);/ 讀取當(dāng)前時(shí)間;結(jié)果t以秒數(shù)表示cout<<endl<<ctime(&t);/ 將t值轉(zhuǎn)換為以下格式:星期 月 日 小時(shí):分:秒 年cout<<"temperarture = "<<temp<<" &

47、quot;cout<<"pressure = "<<pres<<" "cout<<"humidity = "<<hum<<endl;/* Results:后臺(tái)工作!后臺(tái)工作!后臺(tái)工作!Tue Nov 24 10:55:23 2009temperarture = 25 pressure = 986 humidity = 48后臺(tái)工作!后臺(tái)工作!Tue Nov 24 10:55:24 2009temperarture = 26.5 pressure = 990 hum

48、idity = 50后臺(tái)工作!后臺(tái)工作!Tue Nov 24 10:55:25 2009temperarture = 28 pressure = 994 humidity = 52后臺(tái)工作!Tue Nov 24 10:55:26 2009temperarture = 25 pressure = 986 humidity = 48后臺(tái)工作!后臺(tái)工作!后臺(tái)工作!Tue Nov 24 10:55:27 2009temperarture = 26.5 pressure = 990 humidity = 50后臺(tái)工作!后臺(tái)工作!Tue Nov 24 10:55:28 2009temperarture =

49、 28 pressure = 994 humidity = 52后臺(tái)工作!后臺(tái)工作!Tue Nov 24 10:55:29 2009temperarture = 25 pressure = 986 humidity = 48*/以上程序中,主線程創(chuàng)建三條子線程,它們分別是:ThreadTimer、ThreadAquisition和ThreadShow。主線程還為這三個(gè)子線程創(chuàng)建三個(gè)事件對(duì)象,它們分別是:g_hEvent_Timer、g_hEvent_Acq和g_hEvent_Show。當(dāng)然,它們是自動(dòng)復(fù)位事件。主線程接著將事件對(duì)象g_hEvent_Timer置位,等待該事件的子線程就是Thre

50、adTimer。該子線程即執(zhí)行計(jì)時(shí)工作,此計(jì)時(shí)較為粗糙,只是簡(jiǎn)單地延時(shí)1000毫秒(第十章中程序DigiClock.cpp的計(jì)時(shí)較為準(zhǔn)確)。接著該子線程將事件對(duì)象g_hEvent_Acq置位,等待該事件的子線程就是ThreadAquisition。該子線程即執(zhí)行數(shù)據(jù)采集工作,采集溫度、壓力和濕度三個(gè)參量值。此處采集工作只是模擬而已。接著該子線程將事件對(duì)象g_hEvent_ Show置位,等待該事件的子線程就是Thread Show。該子線程即將采集的三個(gè)數(shù)據(jù)值加以顯示,同時(shí)在顯示數(shù)據(jù)之前先顯示當(dāng)前時(shí)間。在延時(shí)1000毫秒之后,子線程ThreadTimer又開始新的一輪工作。就這樣,一條主線程和

51、三條子線程按照順序、有條不紊地工作著。程序中主線程和子線程ThreadShow所顯示的數(shù)據(jù)是滾動(dòng)的。如果不希望這樣顯示數(shù)據(jù),而希望在屏幕上固定位置顯示數(shù)據(jù)值。這就需要利用窗口功能,請(qǐng)見(jiàn)第十章§10.4“典型程序之三-多任務(wù)和子窗口”的例子MultiTask.exe。9.4.4跨越進(jìn)程邊界的事件句柄用戶不但可以如上所述地在線程之間使用事件句柄實(shí)現(xiàn)同步,而且可以在進(jìn)程之間使用事件句柄??梢栽诓煌M(jìn)程中使用OpenEvent或CreateEvent來(lái)獲得事件對(duì)象的句柄。例如,假定事件對(duì)象是使用名稱“Shared”創(chuàng)建的,一個(gè)進(jìn)程可使用函數(shù)OpenEvent來(lái)獲得此事件的句柄hEvent:H

52、ANDLE hEvent = OpenEvent (EVENT_ALL_ACCESS, FALSE, “Shared”); 并且調(diào)用函數(shù)SetEvent使該事件句柄發(fā)送信號(hào)。另一個(gè)進(jìn)程可以使用相同的參數(shù)“Shared”調(diào)用函數(shù)CreateEvent來(lái)獲得同一事件的句柄:HANDLE hEvent = CreateEvent (NULL, FALSE, FALSE, “Shared”); 并且調(diào)用函數(shù)WaitForSingleObject等待該事件句柄發(fā)出信號(hào)。請(qǐng)看以下兩個(gè)應(yīng)用程序,它們是使用跨越進(jìn)程邊界事件句柄的例子。它們分別是Bank和Customer。這兩個(gè)程序如下:第一個(gè)程序是wnd_B

53、ank.cpp:/ wnd_Bank.cpp/ 打開顧客帳戶并且讀出其中存款數(shù)量,事件句柄發(fā)送信號(hào),通知Cutomer進(jìn)程。/ 等待顧客操作完畢,接受Cutomer進(jìn)程另一句柄發(fā)送信號(hào)后,讀取新的存款數(shù)量。/ 改編自Mikey Williams著Windows 2000編程技術(shù)內(nèi)幕#include <windows.h>#include <stdio.h>/for getchar( )#include <fstream.h>void main( )/打開顧客帳戶并且讀出其中存款數(shù)量int balance;ifstream in_1("account

54、.dat"); in_1>>balance;cout<<"目前賬戶內(nèi)存款:"<<balance<<endl;in_1.close( );getchar( );HANDLE hEventBank = OpenEvent (EVENT_ALL_ACCESS, FALSE, "BANKEVENT");SetEvent (hEventBank);/句柄發(fā)信號(hào)cout<<"通知顧客可以存取款:"/創(chuàng)建顧客事件對(duì)象的句柄,等待該句柄發(fā)信號(hào)HANDLE hEventCustome

55、r = CreateEvent (NULL, FALSE, FALSE, "CUSTOMEREVENT");cout<<"等待顧客操作!"<<endl;WaitForSingleObject (hEventCustomer, INFINITE);ifstream in_2("account.dat"); in_2>>balance;cout<<"顧客操作結(jié)束!目前賬戶內(nèi)存款:"<<balance<<endl;in_2.close( );getc

56、har( );第二個(gè)程序是wnd_Customer.cpp:/ wnd_Customer.cpp/ 等待直至接收到Bank進(jìn)程的句柄發(fā)送信號(hào),然后顧客存款或取款。/ 操作完畢后,計(jì)算新的存款數(shù)量并將其輸出至賬戶中。使另一事件句柄發(fā)送信號(hào),通知Bank進(jìn)程。/ 改編自Mikey Williams著Windows 2000編程技術(shù)內(nèi)幕#include <windows.h>#include <stdio.h>/for getchar( )#include <fstream.h>void main( )/創(chuàng)建銀行事件對(duì)象的句柄,等待該句柄發(fā)信號(hào)cout<&l

57、t;"等待銀行通知!"<<endl;HANDLE hEventBank = CreateEvent (NULL, FALSE, FALSE, "BANKEVENT");WaitForSingleObject (hEventBank, INFINITE);cout<<"銀行通知可以存取款"<<endl;CloseHandle (hEventBank);int balance, deposit;ifstream in("account.dat"); in>>balance

58、;in.close( );cout<<"請(qǐng)顧客存取(正數(shù)為存款,負(fù)數(shù)為取款):"cin>>deposit;if (deposit<0)cout<<"顧客取出"<<-deposit<<endl;elsecout<<"顧客存入"<<deposit<<endl;ofstream out("account.dat"); out<<(balance+deposit);out.close( );getchar( );cout<<&q

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝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ù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 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ì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論