2023年全國碩士研究生考試考研英語一試題真題(含答案詳解+作文范文)_第1頁
已閱讀1頁,還剩124頁未讀, 繼續(xù)免費閱讀

下載本文檔

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

文檔簡介

1、,第8章 多線程程序設計技術,8.1 服務器線程模型8.2 多線程應用環(huán)境8.3 線程基本操作函數8.4 線程同步8.5 并發(fā)線程模型服務器設計8.6 完成端口服務器設計小結,通過前面幾章的學習,讀者已經基本掌握了在主要幾種網絡協(xié)議下,進程之間進行網絡數據通信的程序設計的基本方法。然而,實際的網絡服務程序的開發(fā)設計要復雜得多,需要運用一些輔助技術,例如處理并發(fā)服務的多線程技術、快速開發(fā)網絡程序的封裝技術等。因此

2、,從本章開始將陸續(xù)介紹一些網絡編程的相關技術?! ≡贑/S工作模式下,總是少量的服務器為眾多的、數量不可預測的多個客戶端服務??蛻舳朔照埱蟮牡竭_不但難以預測,而且具有顯著的并發(fā)性,為此服務器必須設計成能夠服務于并發(fā)請求,且具有伸縮性。,,本章將從多線程概念入手,介紹可伸縮服務,繼而介紹線程同步的方法以及完成端口技術。本章內容多使用在服務器端,因此在程序設計的方法介紹上更加側重于服務器編程。,,網絡服務器設計可以采用兩種線程模型,即串

3、行模型和并發(fā)模型。  串行模型采用單個線程等待客戶端的請求,當請求到來時,該線程醒來并處理請求,這種模型可以應用于簡單的服務器程序,其優(yōu)點是服務器接受的請求比較少,而且請求能被很快地處理。,,8.1 服務器線程模型,串行模型的缺點也十分明顯,當多個客戶端同時向服務器發(fā)出請求時,這些請求必須依次被接受,并且后一個請求總是在前一個請求處理完畢后才能被接受。然而,在網絡服務應用程序工作中,隨時可能面臨為多個不同時間到達的請求提供服務的局面

4、(互聯(lián)網上幾乎所有服務程序都面臨這樣的問題),顯然,這種局面是串行模型無法應對的?! 榱私鉀Q串行模型存在的問題,可以使用并發(fā)模型。并發(fā)模型使用單個線程等待客戶端請求,當請求到來時,創(chuàng)建新線程來處理請求。等待客戶請求的線程繼續(xù)等待另一個客戶端請求,新線程完成請求處理。當新線程處理完客戶端請求后,隨即退出。,,由于并發(fā)模型為每個客戶端都會創(chuàng)建一個新的線程,客戶端請求能夠很快地被處理,并且每個客戶端請求都有自己的線程,因此服務效率要明顯好

5、于串行模型,且基于并發(fā)模型的服務器程序具有良好的伸縮性,當升級硬件時,服務器程序的性能可以得到提高?! ∠旅嬉訵inSock的面向連接服務器編程為例,說明并發(fā)模型的工作原理(如圖8-1所示)?! ∈紫劝才胖骶€程作為監(jiān)聽線程(執(zhí)行路徑),創(chuàng)建一個套接字監(jiān)聽客戶端的連接請求。當有一個連接請求到達時,讓主線程創(chuàng)建一個新的套接字。,,監(jiān)聽套接字將接受的客戶端連接遞交給新的套接字,然后創(chuàng)建一個線程并由該線程提供具體的服務,而主線程繼續(xù)監(jiān)聽來自

6、客戶端的連接請求。線程服務結束后,釋放資源。當主線程再次收到下一個連接請求時(前面線程的服務不一定結束),再創(chuàng)建一個新套接字接收連接請求,并創(chuàng)建相應線程提供服務。依次類推,從而實現(xiàn)服務器的并行服務。這樣不但滿足了少量服務器端為多個客戶端服務的需求,也為各個用戶數據傳輸的獨立性提供了保證,可以說是對并發(fā)服務請求問題的初步解決(更深層次的問題討論參見8.6節(jié))。,,,圖8-1 并發(fā)模型工作原理圖,將一個接受的連接遞交給另外一個套接字的方法

7、很簡單,只要利用accept()函數的返回值即可,程序代碼如下:  SOCKET m_socket, //the socket for waiting connection   AcceptSocket; //the socket for accepting a connection  …  AcceptSocket=accept(m_socket,NULL,NULL);

8、  程序中的m_socket用于主線程監(jiān)聽網絡連接,AcceptSocket用于接收一個m_socket已經接受的網絡連接,更詳細的代碼可以參考8.5節(jié)中的程序示例。,,多線程是指程序中包含多個執(zhí)行流,即在一個程序中可以同時運行多個不同的執(zhí)行路徑(線程)來執(zhí)行不同的任務,也就是說,允許單個程序創(chuàng)建多個并行執(zhí)行的子程序來完成各自的任務。例如,一個公司里有很多各司其職的職員,那么可以認為這個正常運作的公司就是一個進程,而公司里的職員就是線程

9、。公司(進程)作為一個功能整體完成某一宏觀任務,而員工在這個整體框架下,各負其責完成自己的工作。,,8.2 多線程應用環(huán)境,一個公司至少得有一個職員,同理,一個進程至少包含一個線程(事實上目前幾乎已經沒有單線程的網絡商業(yè)應用軟件),瀏覽器就是一個很好的多線程的例子。在瀏覽器中,用戶可以同時完成下載Java小應用程序或圖像、滾動頁面、播放動畫和聲音、打印文件等,就是基于多線程技術實現(xiàn)的。多線程的使用可以提高CPU的利用率,這是因為在多線

10、程程序中,當一個線程必須等待時,CPU可以運行其他的線程而不是等待,從而大大提高了程序的效率?! 《嗑€程程序設計通過分割、組織工作任務,合理利用了任務的阻塞時間,有效地提高了程序的運行效率,但是本質上它并沒有提升硬件設備本身的性能,因此并不是任何場合都適用,歸納起來它能夠適合以下幾種編程環(huán)境:,,(1) 通過網絡(例如,與Web服務器、數據庫或遠程對象)進行通信;  (2) 執(zhí)行需要較長時間因而可能導致 UI 凍結的本地操作;  

11、(3) 區(qū)分各種優(yōu)先級的任務;  (4) 提高應用程序啟動和初始化的性能。  進行多線程程序設計時應當注意:線程越多,占用內存也越多,線程之間需要協(xié)調和管理,對共享資源的訪問會相互影響,太多的線程會導致控制復雜和系統(tǒng)不穩(wěn)定(線程之間的切換會耗費大量的CPU計算時間)。這些都是使用多線程時的不利因素,因此應當慎重。,,線程在操作系統(tǒng)中的存在有多種狀態(tài),好比人的生老病死,相對應于初始態(tài)、可運行態(tài)、阻塞/非可運行態(tài)和死亡態(tài)等四種狀態(tài)。程序

12、設計中,進入或改變這些狀態(tài)需要一些函數的支持,學會運用這些函數也就掌握了多線程的編程技術。Windows為用戶提供了完備的基本線程操作,下面具體來看一下這些基本線程操作函數。,,8.3 線程基本操作函數,8.3.1 創(chuàng)建線程函數  創(chuàng)建函數用來創(chuàng)建一個新的線程,使得進程進入初始態(tài)(也可通過設定參數直接進入可運行態(tài)),該函數的原型為  HANDLE CreateThread( LPSECURITY_ATTRIBUTES lpThr

13、eadAttributes, //線程安全屬性  DWORD dwStackSize, //初始線程堆棧大小  LPTHREAD_START_ROUTINE lpStartAddress, //線程函數指針  LPVOID lpParameter, //傳遞入線程的參數,,DWORD dwCreationFlags, //線

14、程創(chuàng)建標記  LPDWORD lpThreadId); //返回線程ID  其中,參數lpThreadAttributes只在Windows NT下有用,dwStackSize如果為0,則與進程主線程棧相同;lpStartAddress指定線程開始運行的地址;dwCreateFlages可以設定是否創(chuàng)建后掛起線程,若掛起后調用ResumeThread繼續(xù)執(zhí)行。除了CreateThread,創(chuàng)建

15、線程的函數還有CreateRemoteThread等。,,8.3.2 設置線程的優(yōu)先級函數  在多線程編程中,每一個線程都有一個優(yōu)先級來確定該線程在進程中的優(yōu)先地位。通常,一個進程的每個優(yōu)先級包含了五個線程的優(yōu)先級水平。當兩個線程在使用CPU資源發(fā)生沖突時,執(zhí)行優(yōu)先級高的線程,例如:Normal級的線程可以被除了Idle級以外的任意線程搶占。在進程的優(yōu)先級類確定之后,還可以改變線程的優(yōu)先級水平。具體可使用SetThreadPrior

16、ity設置線程優(yōu)先級,函數的原型為  BOOL SetThreadPriority( HANDLE hThread,int nPriority);,,其中,參數hThread為所操作的線程對象句柄;參數nPriority為需要設定的線程優(yōu)先級,可參考表8-1。,,表8-1 線程的優(yōu)先級,,8.3.3 掛起/恢復線程  Windows使用ResumeThread()函數使線程進入可運行態(tài),函數原型如下:  DWORD Resum

17、eThread(HANDLE hThread); //恢復運行線程句柄  對于進入運行態(tài)的線程可以使用SuspendThread函數退出運行態(tài)(好比按下錄音機的暫停鍵),函數原型如下:  DWORD SuspendThread(HANDLE hThread); //掛起線程句柄  對于掛起的線程,其現(xiàn)場數據會被很好地保存,只要調用ResumeThread函數就可恢復運行。,,8.3.4 等

18、待函數  Win32提供了一組等待函數用來讓一個線程阻塞自己的執(zhí)行,從而進入阻塞/非可運行態(tài),以等待某種條件的滿足。這些函數對于后續(xù)介紹的線程同步很有用。等待函數主要分為以下三類?! ?.等待單個對象   這類函數包括SignalObjectAndWait()、WaitForSingleObject(),以及在這些函數名基礎上用“WSA”開頭或“Ex”結尾的擴展函數等。列舉這類函數的代表性函數的原型如下:,,DWORD Signa

19、lObjectAndWait(  HANDLE hObjectToSignal,//置位對象句柄  HANDLE hObjectToWaitOn,//等待對象句柄  DWORD dwMilliseconds, //等待時間  BOOL bAlertable ); //完成例程并加入隊列時是否返回  DWORD WaitForSingleObject(  HANDLE hHandle, //等

20、待對象  DWORD dwMilliseconds ); //等待時間,,上述函數的等待對象句柄可以指向Change notification、Console input、Event、Job、Mutex、Process、Semaphore、Thread、Waitable timer多種類型。另外,等待時間的設置直接影響著函數的返回,在等待時間達到后返回。如果等待時間不限制,則只有同步對象獲得信號才返回;如果等待時間為0,則在測試了

21、同步對象的狀態(tài)之后馬上返回。,,2.等待多個對象  這類函數包括WaitForMultipleObjects()和MsgWaitForMultipleObjects(),以及在這些函數名基礎上用“WSA”開頭或“Ex”結尾的擴展函數等函數,實際上在6.6.3節(jié)我們已經接觸過WSA WaitForMultipleEvents()函數,用于Winsock I/O事件的監(jiān)控。這類函數的代表性函數的原型如下:  DWORD WaitForM

22、ultipleObjects( DWORD nCount, //等待對象的數量  CONST HANDLE *lpHandles,//等待對象句柄數據組,,BOOL fWaitAll, //等待標志  DWORD dwMilliseconds ); //等待時間  DWORD MsgWaitForMultipleObjects( ?DWORD nCount, //等待

23、對象數量  LPHANDLE pHandles, //等待對象句柄數據組  BOOL fWaitAll, //等待標志  DWORD dwMilliseconds,//等待時間  DWORD dwWakeMask ); //輸入等待事件類型,,它們的參數包括同步對象的句柄,等待時間,等待一個還是多個同步對象,等等。,,3.可以發(fā)出提示的等待函數  這類函數包括MsgWaitForMultiple

24、ObjectsEx()、SignalObjectAndWait()、WaitForMultipleObjectsEx()、WaitForSingleObjectEx(),這些函數主要用于重疊(Overlapped)的I/O操作,函數原型略,可參考MSDN。  等待函數會自動將等待對象設置為“置位”狀態(tài),使用時要引起注意。,,8.3.5 終止一個線程函數  當一個線程任務完成或出現(xiàn)問題時,可以對其實施終止操作從而使其進入死亡態(tài)。終止

25、一個線程可以參用以下幾個方法:  (1) 調用ExitThread()函數,函數的原型為  VOID ExitThread( DWORD dwExitCode); //線程退出號  可以使用GetExitCodeThread()函數檢索線程的退出號?! ?2) 調用TerminateThread()強行終止線程運行函數,函數的原型為,,BOOL TerminateThread( HANDLE hThread

26、, DWORD dwExitCode);  當用TerminateThread終止線程時,dll的入口函數DllMain()不會被執(zhí)行(如果有dll的話)。每一個線程都不再使用局部存儲數據時,線程釋放它分配的動態(tài)內存?! 〈送?,終止一個線程的方法還有:引起主線程返回,從而導致ExitProcess被調用,進而導致ExitThread被調用;直接調用ExitProcess導致進程的所有線程終止;調用TerminateProcess終止

27、一個進程時,導致其所有線程終止。,,如1.2.4節(jié)所述,線程作為進程中的一個執(zhí)行流,雖然每個線程都有自己的專有寄存器(棧指針、程序計數器等),但代碼區(qū)(還有全局數據區(qū))是共享進程所共同擁有的,特別是相同優(yōu)先級的線程在訪問共同的數據區(qū)時,可能產生競爭和沖突。,,8.4 線 程 同 步,例如:線程A試圖給一個公共int變量m實施加1操作,而線程B卻試圖給m實施減1操作。如果任一個線程的操作實施成功,就會導致另外一個線程產生錯誤的計算結果,

28、從而導致嚴重的后果。因此必須采用有效的方法協(xié)調線程對公共資源的訪問,即進行線程同步?! ∠旅娼榻B網絡程序設計中常用的四種同步對象,分別是臨界區(qū)同步、事件同步、互斥同步、信號量同步。,,8.4.1 臨界區(qū)同步  臨界區(qū)是一段獨占對某些共享資源進行訪問的代碼,在任意時刻只允許一個線程對共享資源進行訪問。如果有多個線程試圖同時訪問臨界區(qū),那么在有一個線程進入后,其他所有試圖訪問此臨界區(qū)的線程將被掛起,并一直持續(xù)到進入臨界區(qū)的線程離開。臨

29、界區(qū)在被釋放后,其他線程可以繼續(xù)搶占,并以此達到用原子方式操作共享資源的目的。,,以臨界區(qū)對象來保持線程同步用到的函數主要有InitializeCriticalSection()、EnterCriticalSection()、LeaveCriticalSection()等,分別完成初始化、進入、退出臨界區(qū)的功能。函數的原型為  VOID InitializeCriticalSection(LPCRITICAL_SECTION lpCr

30、iticalSection);   VOID EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection);  VOID LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection);,,臨界區(qū)對象控制線程同步的方法為:首先,定義一個關鍵段對象;然后,初始化該對象并把對象設置為NOT_SINGALED,表示允許線程使用資源

31、;如果一段程序代碼需要對某個資源進行同步保護,則這是一段臨界區(qū)代碼,在進入該臨界區(qū)代碼前調用函數EnterCriticalSection(),這樣其他線程都不能執(zhí)行該段代碼,若它們試圖執(zhí)行就會被阻塞;完成關鍵段的執(zhí)行之后,調用函數LeaveCriticalSection();其他的線程就可以繼續(xù)執(zhí)行該段代碼。如果該函數不被調用,則其他線程將無限期地等待,例程如下:,,CRITICAL_SECTION cs; //臨界區(qū)結構對象

32、  char abc;//共享資源   UINT ThreadProc1(LPVOID pParam)  {  EnterCriticalSection(&cs); //進入臨界區(qū)  abc = 'a'; //對共享資源進行寫入操作,,Sleep(30);  LeaveCriticalSec

33、tion(&cs); //離開臨界區(qū)  return 0;  }  UINT ThreadProc2(LPVOID pParam)  {  … //與ThreadProc1一致  abc = 'b'; //對共享資源進行寫入操作  …

34、 //與ThreadProc1一致  },,… //略去ThreadProc3-9  UINT ThreadProc10(LPVOID pParam)  {   … //與ThreadProc1一致  abc = 

35、9;j'; //對共享資源進行寫入操作  … //與ThreadProc1一致  },,……  int main(int argc, char* argv[])   {  … …  \\創(chuàng)建線程  hThread1=CreateThread(NULL,NULL,ThreadProc1,NULL, CR

36、EATE_SUSPENDED,&dwThreadId1);   … …,,hThreadN=CreateThread(NULL,NULL,ThreadProc10,NULL,C REATE_SUSPENDED,&dwThreadId10);  … …  InitializeCriticalSection(&cs); //初始化臨界區(qū)  ResumeThread(hThread

37、1) ; //啟動線程  ……  ResumeThread(hThreadN);  Sleep(300); //等待計算完畢,,printf(“%c/n”, abc); //報告計算結果  return 0;  }  這里僅給出了線程(主要是線程ThreadProc1)和主線程的部分代碼,并假設各個線程都試圖修改公共變量abc(下面

38、同步方法的介紹都是基于此問題進行說明),程序打印出abc的最終結果。,,8.4.2 事件同步  事件對象對線程的控制是通過對事件對象位狀態(tài)的控制來實現(xiàn)的。如果事件為置位狀態(tài),則線程可以對共享資源進行操作;如果為復位狀態(tài),則不能。事件對線程保持同步的控制原理如圖8-2所示。線程B在執(zhí)行到事件控制部分時,由于事件已經被A復位,因此將會發(fā)生阻塞,而A線程此時則可以在沒有B線程干擾的情況下對共享資源進行處理,并在處理完成后對事件進行置位,使

39、其被釋放。B因而得以對A先前已處理完畢的共享資源進行操作(B線程開始執(zhí)行時對事件進行復位)。,,,圖8-2 事件對線程保持同步的操作原理,事件內核對象控制線程同步的方法為:首先,調用CreateEvent()函數創(chuàng)建一個事件對象,該函數的返回值為一個事件句柄hEvent;然后,線程函數則通過WaitForSingleObject()等待函數無限等待hEvent的置位,只有在事件置位時,WaitForSingleObject()才會返回

40、,被保護的代碼將得以執(zhí)行;WaitForSingleObject()等到hEvent置位后就會立即將其復位,然后開始執(zhí)行保護代碼。復位有自動復位和人工復位兩種形式,可在創(chuàng)建事件對象時指定復位形式。,,自動復位是指當對象獲得信號后,就釋放下一個可用線程(優(yōu)先級別最高的線程,如果優(yōu)先級別相同,則等待隊列中的第一個線程被釋放);人工復位是指當對象獲得信號后,就釋放所有可利用線程。線程執(zhí)行完保護代碼后,將事件對象置位,例程如下:  HANDL

41、E hEvent = NULL; //事件句柄  char abc; //共享資源   UINT ThreadProc1(LPVOID pParam),,{  WaitForSingleObject(hEvent,INFINITE); //等待事件置位  abc = 'a';

42、 //對共享資源進行寫入操作  Sleep(30);  SetEvent(hEvent); //處理完成后即將事件對象置位  return 0;  },,// ThreadProc2-10略  ……  int main(int argc, char* argv[])   {  … …  hEvent = CreateEven

43、t(NULL, FALSE, FALSE, NULL); //創(chuàng)建事件  SetEvent(hEvent); //事件置位,,//創(chuàng)建線程  hThread1=CreateThread(NULL,NULL,ThreadProc1,NULL, CREATE_SUSPENDED,&dwThreadId1);   … …  hThreadN=Creat

44、eThread(NULL,NULL,ThreadProc10,NULL,C REATE_SUSPENDED,&dwThreadId10);  … …  InitializeCriticalSection(&cs); //初始化臨界區(qū),,ResumeThread(hThread1) ; //啟動線程  ……  ResumeThread(hT

45、hreadN);  Sleep(300); //等待計算完畢  printf(" %c/n", abc); //報告計算結果  return 0;  },,互斥對象在MFC中可以通過CEvent類進行表述,方法與上述程序類似,敘述從略。使用臨界區(qū)只能同步同一進程中的線程,而使用事件內核對象則可

46、以對進程外的線程進行同步,其前提是得到對此事件對象的訪問權。,,8.4.3 互斥同步  互斥是一種用途非常廣泛的內核對象,能夠保證多個線程對同一共享資源的互斥訪問。同臨界區(qū)有些類似,只有擁有互斥對象的線程才具有訪問資源的權限。由于互斥對象只有一個,因此就決定了任何情況下此共享資源都不會同時被多個線程所訪問。當前占據資源的線程在任務處理完后應將擁有的互斥對象交出,以便其他線程在獲得后得以訪問資源。與其他幾種內核對象不同,互斥對象在操作

47、系統(tǒng)中擁有特殊代碼,,,并由操作系統(tǒng)來管理,操作系統(tǒng)甚至還允許其進行一些其他內核對象所不能進行的非常規(guī)操作。為便于理解,可參照圖8-3給出的互斥內核對象的工作模型?! D8-3(a)中的箭頭為要訪問資源(矩形框)的線程,但只有第二個線程擁有互斥對象(黑點)并得以進入到共享資源,而其他線程則會被排斥在外(如圖8-3(b)所示)。當此線程處理完共享資源并準備離開此區(qū)域時將把其所擁有的互斥對象交出(如圖8-3(c)所示),其他任何一個試圖訪

48、問此資源的線程都有機會得到此互斥對象。,,,圖8-3 互斥內核對象的工作模型,以互斥對象來保持線程同步用到的函數主要有CreateMutex()、OpenMutex()、ReleaseMutex()等。CreateMutex()、OpenMutex()、ReleaseMutex()的原型為  HANDLE CreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes,//安全屬性指針  B

49、OOL bInitialOwner, //互斥對象所有者標識  LPCTSTR lpName ); //互斥對象名稱指針  HANDLE OpenMutex( DWORD dwDesiredAccess, //互斥對象訪問標識  BOOL bInheritHandle, //返回對象繼承性  LPCTSTR lpName ); /

50、/互斥對象名稱指針,,BOOL ReleaseMutex( HANDLE hMutex ); //互斥對象句柄  互斥對象控制線程同步的具體方法為:首先要通過CreateMutex()(或OpenMutex())創(chuàng)建或打開一個互斥對象。然后調用等待函數,可以的話利用關鍵資源;最后,調用RealseMutex()釋放互斥對象,例程如下:  HANDLE hMutex = NULL;

51、//互斥對象  char abc;   UINT ThreadProc1(LPVOID pParam)  {,,WaitForSingleObject(hMutex,INFINITE); //等待互斥對象通知  abc = 'a'; //對共享資源進行寫入操作  Sleep(30);  ReleaseMutex(hM

52、utex); //釋放互斥對象  return 0;  }  // ThreadProc2-10略  ……,,int main(int argc, char* argv[])   {  … …  hMutex = CreateMutex(NULL, FALSE, NULL); //創(chuàng)建互斥對象或使用OpenMutex打開存在的  互斥對象  //創(chuàng)建線程  hThread1=CreateThread(N

53、ULL,NULL,ThreadProc1,NULL, CREATE_SUSPENDED,&dwThreadId1);   … …,,hThreadN=CreateThread(NULL,NULL,ThreadProc10,NULL,C REATE_SUSPENDED,&dwThreadId10);  … …  ResumeThread(hThread1) ; //啟動線程  

54、……  ResumeThread(hThreadN);  Sleep(300); //等待計算完畢,,printf("%c/n", abc); //報告計算結果  return 0;  }  與臨界區(qū)對象不同,互斥對象可以在進程間使用,而臨界區(qū)對象只能用于同一進程的線程之間。,,8.4.4 信

55、號量同步  信號量是一個可以控制多個進程存取共享資源的計數器,經常作為一種鎖定機制,以防止當一個進程正在存取共享資源時,另一個進程也存取同一資源。在Win32中,當信號量的數值變?yōu)?時再給以信號。在有多個資源需要管理時可以使用信號量對象。信號量內核對象對線程的同步方式與前面幾種方法不同,它允許多個線程在同一時刻訪問同一資源,但是需要限制在同一時刻訪問此資源的最大線程數目。信號量對象對資源的控制如圖8-4所示。,,,圖8-4 信號量對

56、象對資源的控制,圖中分別以灰色陰影箭頭和白色箭頭表示共享資源所允許的最大資源計數和當前可用資源計數。初始如圖8-4(a)所示,最大資源計數和當前可用資源計數均為4,此后每增加一個對資源進行訪問的線程(用灰色陰影箭頭表示),當前資源計數就會相應減1,圖8-4(b)所示為3個線程對共享資源進行訪問時的狀態(tài)。當進入線程數達到4個時,將如圖8-4(c)所示,此時已達到最大資源計數,而當前可用資源計數也已減到0,其他線程無法對共享資源進行訪問。在

57、當前占有資源的線程處理完畢而退出后,將會釋放出空間,圖8-4(d)已有兩個線程退出對資源的占有,當前可用計數為2,可以再允許2個線程進入到對資源的處理。,,以信號量對象來實現(xiàn)線程同步用到的函數有CreateSemaphore()、OpenSemaphore()和ReleaseSemaphore()函數。它們的原型為  HANDLE CreateSemaphore(LPSECURITY_ATTRIBUTES lpSemaphoreAtt

58、ributes,//安全屬性指針  LONG lInitialCount, //初始數量  LONG lMaximumCount, //最大數量  LPCTSTR lpName ); //信號量指針名  HANDLE OpenSemaphore( DWORD dwDesiredAccess, //訪問標識,,BOOL bInheritHandle,

59、//對象繼承性  LPCTSTR lpName ); //信號量指針名  BOOL ReleaseSemaphore( HANDLE hSemaphore, //信號量對象  LONG lReleaseCount, //要增加的數量  LPLONG lpPreviousCount ); //用于接受先前計數器的指針  信號量控制線程同步的具體方法為:首先,調用函數

60、CreateSemaphore()創(chuàng)建一個信號量(或調用函數OpenSemaphore()打開一個信號量)。,,一般是將當前可用資源計數設置為最大資源計數,每增加一個線程對共享資源的訪問,當前可用資源計數就會減1,只要當前可用資源計數是大于0的,就可以發(fā)出信號量信號。但是當前可用計數減小到0時,則說明當前占用資源的線程數已經達到了所允許的最大數目,不再允許其他線程的進入,此時的信號量信號將無法發(fā)出。然后,調用等待函數,若允許,則線程就可

61、以利用共享資源;線程在處理完共享資源后,應在離開的同時通過ReleaseSemaphore()函數將當前可用資源計數,,加1,例程如下:  HANDLE hSemaphore;//信號量對象句柄  UINT ThreadProc1(LPVOID pParam)  {  WaitForSingleObject(hSemaphore, INFINITE); //試圖進入信號量關口   //線程任務處理  Rel

62、easeSemaphore(hSemaphore,1,NULL); //釋放1個信號量計數,,return 0;  }  // ThreadProc2-10略  ……  int main(int argc, char* argv[])   {  … …  hSemaphore = CreateSemaphore(NULL, 2, 2, NULL); //創(chuàng)建信號量對象  //創(chuàng)建線程,,hThread1=Cre

63、ateThread(NULL,NULL,ThreadProc1,NULL, CREATE_SUSPENDED,  &dwThreadId1);   … …  hThreadN=CreateThread(NULL,NULL,ThreadProc10,NULL,C REATE_SUSPENDED,  &dwThreadId10);  … …  ResumeThread(hThread1) ;

64、 //啟動線程,,……  ResumeThread(hThreadN);  return 0;  }  區(qū)別于前面三種線程之間相互排斥的同步方法,信號量同步允許一定數量的線程共同工作,因此使其更適用于對Socket(套接字)程序中線程的同步,達到能夠控制服務器線程池中工作線程的數量的目的。應當注意,在任何時候,當前可用資源計數決不可能大于最大資源計數?! 【€程同步的方法不僅限于上述四種,還有等待定時器(w

65、aitable timer)、更改通知(change notification)、控制臺輸入(consle input)、作業(yè)(job)等同步對象可供使用。,,通過上述學習,我們對多線程的編程技術有了一定的了解。結合WinSock的阻塞操作,程序員在編寫網絡程序時完全可以利用多線程技術,編寫出面向多用戶、集成化服務、可伸縮、非持續(xù)性的服務器程序,如大型FTP、HTTP服務器等,以及多種具有強大功能的網絡程序。本節(jié)以一個簡單的并發(fā)線程模型

66、FTP服務器為例介紹具體的程序設計方法?! ?include "StdAfx.h",,8.5 并發(fā)線程模型服務器設計,#include   #define MAX_FILESIZE 32*1024  struct Filedata  {  char ffname[30];  char ffdata[MAX_FILESIZE];  int len;  }DataPacket;  DWOR

67、D GetFile(char * fname,char * thrname)  {,,int i;  FILE * fp;  int Filesize;  int count,total=0;  char buffer[100];  char Senddata[MAX_FILESIZE];  fp=fopen(fname,"r");  if(fp==NULL)  {

68、  printf("cannot open %s for %s\n",fname,thrname);  return(0);,,}  i=0;  Filesize=0;  memset(Senddata,0,MAX_FILESIZE);  while(!feof(fp))  {  count=fread(buffer,sizeof(char),100,fp);  

69、if(ferror(fp))  {  printf("read file for %s error",thrname);  break;,,}  Filesize+=count;  if(Filesize>MAX_FILESIZE)  {  printf("the file for %s is too big\n",thrn

溫馨提示

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

評論

0/150

提交評論