[學習]網絡程序設計-第六章_第1頁
已閱讀1頁,還剩34頁未讀, 繼續(xù)免費閱讀

下載本文檔

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

文檔簡介

1、1,第6章 Winsock的多線程編程,6.1.1 WinSock的兩種輸入輸出模式,“阻塞”模式,又稱為同步模式,執(zhí)行I/O操作完成前會一直進行等待,不會將控制權交給程序,工作在“阻塞”模式的套接字稱為阻塞套接字。套接字默認為阻塞模式??梢酝ㄟ^多線程技術進行處理。 “非阻塞”模式,又稱為異步模式,執(zhí)行I/O操作時,Winsock函數會返回并交出控制權。工作在“非阻塞”模式下的套接字稱為非阻塞套接字。 使用 起來比較復雜,因

2、為函數在沒有運行完成就進行返回,會不斷地返回WSAEWOULDBLOCK錯誤,但功能強大。,2,3,在大多數情況下,非阻塞模式調用都會失敗,返回一個WSAEWOULDBLOCK錯誤,表示操作的條件尚不具備,但又不允許等待完成請求的操作。非阻塞模式下會頻繁返回錯誤,應仔細檢查返回代碼;并且在不成功的情況下 不應反復輪詢.,“非阻塞”模式,6.1.2 兩種模式的優(yōu)缺點及解決方法“阻塞”與“非阻塞”模式各有其優(yōu)點和缺點。阻塞套接字的I

3、/O操作工作情況比較確定,無非是調用、等待、返回。大部分情況下,I/O操作都能成功地完成,不過就是花費了等待的時間因而比較容易使用,容易編程;但在應付諸如需要建立多個套接字連接來為多個客戶服務的時候,或在數據的收發(fā)量不均勻的時候,或在輸入輸出的時間不確定的時候,卻顯得性能低下,甚至無能為力。,4,使用非阻塞套接字,需要編寫更多的代碼,因為必須恰當地把握調用I/O函數的時機,盡量減少無功而返的調用,還必須詳加分析每個Winsock調用

4、中收到的WSAEWOULDBLOCK錯誤,采取相應的對策。這種I/O操作的隨機性使得非阻塞套接字顯得難于操作。 所以,我們必須采取一些適當的對策,克服這兩種模式的缺點,讓阻塞和非阻塞套接字能夠滿足各種場合的要求。對于非阻塞的套接字工作模式,進一步引入了五種“套接字I/O模型”。對于阻塞的套接字工作模式,則進一步引入了多線程機制。,5,6.2.1 Win32 OS是單用戶多任務的操作系統(tǒng)最早的DOS是單用戶單任務的。

5、后來發(fā)展到圖形界面的Windows,發(fā)展到Windows 95,Windows 98,就都支持多任務了。從Windows NT起,Windows操作系統(tǒng)更是發(fā)展成了一個真正的搶占式多任務操作系統(tǒng)。一個運行中的應用進程實例,就是一個進程。一個基于Win32的應用程序可以包含一個或多個進程。,6,6.2.2 Win32 OS是支持多線程的操作系統(tǒng)Win32操作系統(tǒng)還支持同一進程的多線程。在一個Windows進程內,可以包含多個

6、線程。一個線程(thread)是進程內的一條執(zhí)行路徑,具體地說,是一個應用程序中的一條可執(zhí)行路徑,往往是應用程序中的一個或多個函數。一個進程中至少要有一個線程,習慣將它稱為主線程。任何一個應用程序進程都有一個主線程。一般C程序中的Main或WinMain函數就規(guī)定了主線程的執(zhí)行代碼。,7,8,6.2.2 Win32 OS是支持多線程的操作系統(tǒng)當你啟動了一個應用程序時,操作系統(tǒng)在為它創(chuàng)建了進程之后,也創(chuàng)建了該進程的主線程,并根

7、據Main或WinMain函數的地址,開始執(zhí)行該進程的主線程。主線程可以創(chuàng)建并啟動其他輔助線程。由主線程創(chuàng)建的線程又可以創(chuàng)建并啟動更多的線程。線程的代碼執(zhí)行完畢時會自動終止,并將占用的資源釋放給進程;進程的所有線程都終止時,進程也就終止了,并會將占用的資源釋放給操作系統(tǒng)。,一個線程需要占用一定的系統(tǒng)資源,一類是此線程專用的,另一類則是與進程的其他線程共享的。 線程是進程中相對獨立的執(zhí)行單位,也是Win32操作系統(tǒng)中可調度的最小

8、的執(zhí)行單位。多個進程中的多個線程并發(fā)地執(zhí)行。對于擁有多個處理機的計算機系統(tǒng),調度程序可以將不同的線程安排到不同的處理機上去運行,一方面平衡了CPU的負載,另一方面也提高了系統(tǒng)的運行效率。,9,6.2.2 Win32 OS是支持多線程的操作系統(tǒng),6.2.3 多線程機制在網絡編程中的應用如果一個應用程序,有多個任務需要同時進行處理,那就最適合使用多線程機制。對于網絡上客戶機軟件,采用多線程的編程技術,能克服在單線程的編程模式下,

9、由于阻塞等待而產生的客戶程序就不能及時響應用戶的操作命令的問題。利用Windows系統(tǒng)的多線程機制可以很好的解決這個問題。將用戶界面的處理放在主線程中,將數據的I/O、費時的計算、網絡訪問等放在輔助線程里。,10,11,網絡上服務器軟件應采用多線程的編程技術。能更好地為多個客戶服務??梢詧?zhí)行許多后臺處理,如數據庫訪問、安全驗證、日志記錄、事物處理等??蛻魴C軟件,采用多線程機制也能大大提高應用程序的運行效率。如東方快車、網絡螞

10、蟻等文件下載軟件,就采用了多線程機制,用多個線程同時下載一個文件的不同部分,大大加快了下載速度??傊?,多線程機制在網絡編程中是大有作為的。,6.2.3 多線程機制在網絡編程中的應用,VC++ 6.0為程序員提供了Windows應用程序的集成開發(fā)環(huán)境,在這個環(huán)境下,有兩種開發(fā)程序的方法。既可以直接使用Win32 API來編寫C風格的Win32應用程序,也可以利用MFC基礎類庫編寫C++風格的應用程序。 在這兩種Window

11、s應用程序的開發(fā)方式下,多線程的編程原理是一致的。,12,6.3.1 MFC支持的兩種線程 微軟的基礎類庫MFC提供了對于多線程應用程序的支持。在MFC中,線程分為兩種, 一種是用戶接口線程(user-interface thread),或稱用戶界面線程; 另一種是工作線程(the worker thread),這兩類線程可以滿足不同任務的處理需求。,13,1.用戶接口線程 用戶接口線程通

12、常用來處理用戶輸入產生的消息和事件,并獨立地響應正在應用程序其它部分執(zhí)行的線程產生的消息和事件,MFC特別地為用戶接口線程提供了一個消息泵(a message pump)。用戶接口線程包含一個消息處理的循環(huán),以應對各種事件。 在MFC應用程序中,所有的線程都是由CWinThread對象來表示的。CWinThread類(可以理解為C++的Windows 線程類)是用戶接口線程的基類,CWinApp就是從CWinThread類派

13、生出來的,我們在編寫用戶接口線程的時候,也需要從CWinThread類派生出自己的線程類,借助ClassWizard可以很容易地做這項工作。,14,2.工作線程 工作線程(the worker thread),適用于處理那些不要求用戶輸入并且比較消耗時間的其他任務。對用戶來說,工作線程運行在后臺。這就使得工作線程特別適合去等待一個事件的發(fā)生。 CWinThread類同樣是工作線程的基類,同樣是由CWinThrea

14、d對象來表示的。但在編寫工作線程的時候,你甚至不必刻意地從CWinThread類派生出自己的線程類對象。你可以調用MFC框架的AfxBeginThread幫助函數,它會為你創(chuàng)建CWinThread對象。,15,6.3.2 創(chuàng)建MFC的工作線程 下面介紹利用MFC創(chuàng)建工作線程所必需的步驟。 創(chuàng)建一個工作線程是一個相對簡單的任務,只要經過兩個步驟就能使你的工作線程運行:第一步是編程實現控制函數,第二步是創(chuàng)建并啟動工作線

15、程。一般不必從CWinThread派生一個類。當然,如果你需要一個特定版本的CWinThread類,也可以去派生;但對于大多數的工作線程是不要求的。你可以不作任何修改地使用CWinThread類。,16,1.編程實現控制函數(implementing the controlling function) 一個工作線程對應一個控制函數(the controlling function)。線程執(zhí)行的任務都應編寫在控制函數之中??刂?/p>

16、函數規(guī)定了該線程的執(zhí)行代碼,所謂啟動線程,實際就是開始運行它對應的控制函數,當控制函數執(zhí)行結束而退出時,線程也就隨之終止。編寫實現工作線程的控制函數是創(chuàng)建工作線程的第一步。 編寫工作線程的控制函數必須遵守一定的格式,控制函數的原型聲明是: UINT ControlFunctionName(LPVOID pParam);,17,2.創(chuàng)建并啟動工作線程(Starting the thread) 在進程的主

17、線程或其他線程中調用AfxBeginThread()函數就可以創(chuàng)建新的線程,并使新線程開始運行。一般將線程的創(chuàng)建者稱為新線程的父線程。 AfxBeginThread()函數是MFC提供的幫助函數,有兩個重載的版本,區(qū)別在于使用的入口參數不同。一個用于創(chuàng)建并啟動用戶接口線程,一個用于創(chuàng)建并啟動工作線程。要創(chuàng)建并啟動你的工作線程,必須采用如下的調用格式:,18,CWinThread* AfxBeginThread (AFX_

18、THREADPROC pfnThreadProc,LPVOID pParam,int pPriority = THREAD_PRIORITY_NORMAL,UINT nStackSize = 0,DWORD dwCreateFlags = 0,LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL);,19,3.創(chuàng)建工作線程的例子(1)編程實現線程控制函數。// 首先定義了一

19、個結構:struct {int nN; // 數組元素的個數。 double* pD; // 指向一個雙精度實數的數組。}myData; // 然后定義了此結構類型的變量,對該變量初始化(對其成員變量賦值)的代碼省略了。myData ss;,20,// 接著定義線程的控制函數。UINT MyCalcFunc(LPVOID pParam){// 如果入口參數為空指針,終止線程。if

20、(pParam == NULL) AfxEndThread(MY_NULL_POINTER_ERROR); int nN = pParam->nN; // 數組的元素個數。double* pD = pParam->pD; // 指向數組的第一個元素。double sum=0; // 數組元素之和。for ( int i =0; i<nN; i++) sum+=pD[i

21、]; // 求和。CString bb;bb.Format(“數組的和是:%d”, sum); // 格式化顯示字符串。AfxMessageBox(bb); // 顯示結果。 return 0;},21,(2)在程序進程的主線程中調用AfxBeginThread()函數來創(chuàng)建并啟動運行這個線程。將控制函數名和結構變量的地址作為參數來傳遞,其他的參數省略,表示使用默認值。AfxBeginThread(My

22、CalcFunc,&ss); 一旦調用了此函數,線程就被創(chuàng)建,并開始執(zhí)行線程函數。當數據的計算完成時,函數將停止運行,相應的線程也隨即終止。線程擁有的堆棧和其他資源都將釋放。CWinThread對象將被刪除。,22,4.創(chuàng)建工作線程的一般模式從上面的例子中可以得出創(chuàng)建工作線程的一般模式:(1)工作線程控制函數的框架。UINT MyThreadProc( LPVOID pParam ){CMyObject* p

23、Object = (CMyObject*)pParam; //進行參數的傳遞if (pObject == NULL ||!pObject->IsKindOf(RUNTIME_CLASS(CMyObject)))return 1; // 如果入口參數無效就返回。,23,// 利用入口參數作某些事情。這是工作線程要完成的主要工作。 return 0; // 線程成功地完成并返回。} (2)在程序的另一個函數

24、中插入以下代碼。 ..............pNewObject = new CMyObject;AfxBeginThread(MyThreadProc, pNewObject);,24,6.3.3 創(chuàng)建并啟動用戶界面線程 創(chuàng)建并啟動用戶界面線程一般要經過三個步驟:第一步是從CWinThread類派生出自己的線程類;第二步是改造這個線程類,使它能夠完成用戶所希望的工作;第三步是創(chuàng)建并啟動用戶界面線程。1.從CWin

25、Thread類派生出自己的線程類要創(chuàng)建一個MFC的用戶界面線程,所要做的第一件事就是從CWinThread類派生出自己的線程類,一般借助ClassWizard來做這項工作。,25,2.改造自己的線程類對這個派生的線程類作以下改造工作:(1)在這個線程類的.h頭文件中,使用DECLARE_DYNCREATE宏來聲明這個類;在用戶線程類的.CPP實現文件中,使用IMPLEMENT_DYNCREATE宏來實現這個類。前者的調用格式是:

26、DECLARE_DYNCREATE( class_name ),,26,其中class_name是實際的類名。對一個從CObject類繼承的類使用這個宏,會使得應用程序框架(framework)在運行時動態(tài)地生成該類的新對象。新線程是由主線程或其他線程在執(zhí)行過程中創(chuàng)建的,都應支持動態(tài)創(chuàng)建,因為應用程序框架需要動態(tài)地創(chuàng)建它們。 DECLARE_DYNCREATE宏應放在此類的.H文件中,并應在所有需要訪問此類的對象的.CPP文

27、件中加入包含這個文件的#include語句。,27,(2)如果在一個類的宣布中使用了DECLARE_DYNCREATE宏,那就必須在這個類的.CPP實現文件中,使用IMPLEMENT_DYNCREATE宏。它的調用格式是:IMPLEMENT_DYNCREATE( class_name, base_class_name ) 參數是實際的線程類名和它的基類名。,28,(3)這個線程類必須重載它的基類(CWinThread類)

28、的某些成員函數,如該類的InitInstance()成員函數;對于基類的其他成員函數,可以有選擇地重載,也可以使用由CWinThread類提供的缺省函數。表7.1給出了相關的成員函數:(4)創(chuàng)建新的用戶界面窗口類,如窗口,對話框,并添加所需要的用戶界面控件,然后建立新建的線程類與這些用戶界面窗口類的聯系。(5)利用類向導,為新建的線程類添加控件成員變量,添加響應消息的成員函數,為它們編寫實現的代碼。經過以上步驟的改造,用戶的線程類

29、已經具備了完成用戶任務的能力。,29,3.創(chuàng)建并啟動用戶界面線程 要創(chuàng)建并啟動用戶界面線程,可以使用MFC提供的AfxBeginThread()函數的另一個版本,使用的調用格式是:CWinThread* AfxBeginThread (CRuntimeClass* pThreadClass,int pPriority = THREAD_PRIORITY_NORMAL,UINT nStackSize = 0,

30、DWORD dwCreateFlags = 0,LPSECURITY_ATTTRIBUTES lpSecurityAttrs = NULL);,30,4.AfxBeginThread()函數所作的工作 當進程的主線程或其他線程調用AfxBeginThread()函數來創(chuàng)建一個新的用戶界面線程的時候,該函數做了許多工作。(1)它創(chuàng)建一個新的用戶自己的線程類的對象,由于用戶的線程類是從CWinThread類派生出來的,

31、這個對象當然也繼承了CWinThread類的屬性。,31,(2)然后,MFC就自動調用新線程類中的InitInstance()函數,來初始化這個新的線程類對象實例。這是一個必須在用戶派生的線程類中重載的函數,用戶可在該函數中初始化線程,并分配任何需要的動態(tài)內存。如果初始化成功,InitInstance()函數應返回TRUE,線程就可以繼續(xù)運行;如果初始化失敗,比如內存申請失敗,就返回FALSE,線程將停止執(zhí)行,并釋放所擁有的資源。(3

32、)再調用CWinThread::CreateThread成員函數來開始執(zhí)行這個線程,最終運行CWinThread::RUN函數,進入消息循環(huán)。,32,(4)函數返回一個指向新生成的CWinThread對象的指針,可以把它保存在一個變量中,其它線程就可以利用這個指針來訪問該線程類的成員變量或成員函數。 系統(tǒng)自動地為每一個線程創(chuàng)建一個消息隊列(a message queue),如果線程創(chuàng)建了一個或多個窗口,就必須提供一個消息循

33、環(huán)(a message loop),這個消息循環(huán)從線程的消息隊列中獲取消息,并把它們發(fā)送到相應的windows 過程(window procedures)。,33,因為系統(tǒng)將消息導向獨立的應用程序窗口,所以,在開始線程的消息循環(huán)之前,線程必須至少創(chuàng)建一個窗口,大多數基于Win32的應用程序包含一個單一的線程,該線程創(chuàng)建了若干窗口。一個典型的應用為它的主窗口注冊了窗口類,創(chuàng)建并顯示這個主窗口,并且啟動它的消息循環(huán),所有這一切都在WinMa

溫馨提示

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

評論

0/150

提交評論