[學(xué)習(xí)]網(wǎng)絡(luò)程序設(shè)計(jì)-第六章_第1頁(yè)
已閱讀1頁(yè),還剩34頁(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、1,第6章 Winsock的多線程編程,6.1.1 WinSock的兩種輸入輸出模式,“阻塞”模式,又稱為同步模式,執(zhí)行I/O操作完成前會(huì)一直進(jìn)行等待,不會(huì)將控制權(quán)交給程序,工作在“阻塞”模式的套接字稱為阻塞套接字。套接字默認(rèn)為阻塞模式。可以通過(guò)多線程技術(shù)進(jìn)行處理。 “非阻塞”模式,又稱為異步模式,執(zhí)行I/O操作時(shí),Winsock函數(shù)會(huì)返回并交出控制權(quán)。工作在“非阻塞”模式下的套接字稱為非阻塞套接字。 使用 起來(lái)比較復(fù)雜,因

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

3、/O操作工作情況比較確定,無(wú)非是調(diào)用、等待、返回。大部分情況下,I/O操作都能成功地完成,不過(guò)就是花費(fèi)了等待的時(shí)間因而比較容易使用,容易編程;但在應(yīng)付諸如需要建立多個(gè)套接字連接來(lái)為多個(gè)客戶服務(wù)的時(shí)候,或在數(shù)據(jù)的收發(fā)量不均勻的時(shí)候,或在輸入輸出的時(shí)間不確定的時(shí)候,卻顯得性能低下,甚至無(wú)能為力。,4,使用非阻塞套接字,需要編寫更多的代碼,因?yàn)楸仨毲‘?dāng)?shù)匕盐照{(diào)用I/O函數(shù)的時(shí)機(jī),盡量減少無(wú)功而返的調(diào)用,還必須詳加分析每個(gè)Winsock調(diào)用

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

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

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

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

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

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

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

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

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

13、生出來(lái)的,我們?cè)诰帉懹脩艚涌诰€程的時(shí)候,也需要從CWinThread類派生出自己的線程類,借助ClassWizard可以很容易地做這項(xiàng)工作。,14,2.工作線程 工作線程(the worker thread),適用于處理那些不要求用戶輸入并且比較消耗時(shí)間的其他任務(wù)。對(duì)用戶來(lái)說(shuō),工作線程運(yùn)行在后臺(tái)。這就使得工作線程特別適合去等待一個(gè)事件的發(fā)生。 CWinThread類同樣是工作線程的基類,同樣是由CWinThrea

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

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

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

17、線程或其他線程中調(diào)用AfxBeginThread()函數(shù)就可以創(chuàng)建新的線程,并使新線程開(kāi)始運(yùn)行。一般將線程的創(chuàng)建者稱為新線程的父線程。 AfxBeginThread()函數(shù)是MFC提供的幫助函數(shù),有兩個(gè)重載的版本,區(qū)別在于使用的入口參數(shù)不同。一個(gè)用于創(chuàng)建并啟動(dòng)用戶接口線程,一個(gè)用于創(chuàng)建并啟動(dòng)工作線程。要?jiǎng)?chuàng)建并啟動(dòng)你的工作線程,必須采用如下的調(diào)用格式:,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)編程實(shí)現(xiàn)線程控制函數(shù)。// 首先定義了一

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

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

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

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

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

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

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

26、DECLARE_DYNCREATE( class_name ),,26,其中class_name是實(shí)際的類名。對(duì)一個(gè)從CObject類繼承的類使用這個(gè)宏,會(huì)使得應(yīng)用程序框架(framework)在運(yùn)行時(shí)動(dòng)態(tài)地生成該類的新對(duì)象。新線程是由主線程或其他線程在執(zhí)行過(guò)程中創(chuàng)建的,都應(yīng)支持動(dòng)態(tài)創(chuàng)建,因?yàn)閼?yīng)用程序框架需要?jiǎng)討B(tài)地創(chuàng)建它們。 DECLARE_DYNCREATE宏應(yīng)放在此類的.H文件中,并應(yīng)在所有需要訪問(wèn)此類的對(duì)象的.CPP文

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

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

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

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

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

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

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

溫馨提示

  • 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ù)僅提供信息存儲(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)論