完成端口加線程池技術(shù)實(shí)現(xiàn)_第1頁(yè)
完成端口加線程池技術(shù)實(shí)現(xiàn)_第2頁(yè)
完成端口加線程池技術(shù)實(shí)現(xiàn)_第3頁(yè)
完成端口加線程池技術(shù)實(shí)現(xiàn)_第4頁(yè)
完成端口加線程池技術(shù)實(shí)現(xiàn)_第5頁(yè)
已閱讀5頁(yè),還剩6頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡(jiǎn)介

1/1完成端口加線程池技術(shù)實(shí)現(xiàn)WinSock異步I/O模型[5]完成端口-CompletionPort

如果你想在Windows平臺(tái)上構(gòu)建服務(wù)器應(yīng)用,那么I/O模型是你必須考慮的。Windows操作系統(tǒng)提供了五種I/O模型,分別是:

■選擇(select);

■異步選擇(WSAAsyncSelect);

■事件選擇(WSAEventSelect);

■重疊I/O(OverlappedI/O);

■完成端口(CompletionPort)。

每一種模型適用于一種特定的應(yīng)用場(chǎng)景。程序員應(yīng)該對(duì)自己的應(yīng)用需求非常明確,綜合考慮到程序的擴(kuò)展性和可移植性等因素,作出自己的選擇。

==============================================

█“完成端口”模型是迄今為止最復(fù)雜的一種I/O模型。但是,若一個(gè)應(yīng)用程序同時(shí)需要管理很多的套接字,

那么采用這種模型,往往可以達(dá)到最佳的系統(tǒng)性能!但缺點(diǎn)是,該模型只適用于WindowsNT和Windows2000以上版本的操作系統(tǒng)。

█因其設(shè)計(jì)的復(fù)雜性,只有在你的應(yīng)用程序需要同時(shí)管理數(shù)百乃至上千個(gè)套接字的時(shí)候,而且希望隨著系統(tǒng)內(nèi)安裝的CPU數(shù)量的增多,

應(yīng)用程序的性能也可以線性提升,才應(yīng)考慮采用“完成端口”模型。

█從本質(zhì)上說,完成端口模型要求我們創(chuàng)建一個(gè)Win32完成端口對(duì)象,通過指定數(shù)量的線程,

對(duì)重疊I/O請(qǐng)求進(jìn)行管理,以便為已經(jīng)完成的重疊I/O請(qǐng)求提供服務(wù)。

█※※※大家可以這樣理解,一個(gè)完成端口其實(shí)就是一個(gè)完成I/O的通知隊(duì)列,由操作系統(tǒng)把已經(jīng)完成的重疊I/O請(qǐng)求的通知放入這個(gè)隊(duì)列中。

當(dāng)某項(xiàng)I/O操作一旦完成,某個(gè)可以對(duì)該操作結(jié)果進(jìn)行處理的工線程就會(huì)收到一則通知,工線程再去做一些其他的善后工作,

比如:將收到的數(shù)據(jù)進(jìn)行顯示,等等。而套接字在被創(chuàng)建后,可以在任何時(shí)候與某個(gè)完成端口進(jìn)行關(guān)聯(lián)?!?/p>

通常情況下,我們會(huì)在應(yīng)用程序中創(chuàng)建一定數(shù)量的工線程來處理這些通知。線程數(shù)量取決于應(yīng)用程序的特定需要。理想的情況是,線程數(shù)量等于處理器的數(shù)量,不過這也要求任何線程都不應(yīng)該執(zhí)行諸如同步讀寫、等待事件通知等阻塞型的操作,以免線程阻塞。每個(gè)線程都將分到一定的CPU時(shí)間,在此期間該線程可以運(yùn)行,然后另一個(gè)線程將分到一個(gè)時(shí)間片并開始執(zhí)行。如果某個(gè)線程執(zhí)行了阻塞型的操作,操作系統(tǒng)將剝奪其未使用的剩余時(shí)間片并讓其它線程開始執(zhí)行。也就是說,前一個(gè)線程沒有充分使用其時(shí)間片,當(dāng)發(fā)生這樣的情況時(shí),應(yīng)用程序應(yīng)該準(zhǔn)備其它線程來充分利用這些時(shí)間片。

█使用這種模型之前,首先要?jiǎng)?chuàng)建一個(gè)I/O完成端口對(duì)象,用它面向任意數(shù)量的套接字句柄,管理多個(gè)I/O請(qǐng)求。

要做到這一點(diǎn),需要調(diào)用CreateCompletionPort函數(shù),其定義如下:

HANDLEWINAPICreateIoCompletionPort(

__inHANDLEFileHandle,

__inHANDLEExistingCompletionPort,

__inULONG_PTRCompletionKey,

__inDWORDNumberOfConcurrentThreads

);

要注意該函數(shù)有兩個(gè)功能:

●用于創(chuàng)建一個(gè)完成端口對(duì)象;

●將一個(gè)句柄同完成端口對(duì)象關(guān)聯(lián)到一起。

如果僅僅為了創(chuàng)建一個(gè)完成端口對(duì)象,唯一注意的參數(shù)便是NumberOfConcurrentThreads(并發(fā)線程的數(shù)量),前面三個(gè)參數(shù)可忽略。

NumberOfConcurrentThreads參數(shù)的特殊之處在于,它定義了在一個(gè)完成端口上,同時(shí)允許執(zhí)行的線程數(shù)量。

理想情況下,我們希望每個(gè)處理器各自負(fù)責(zé)一個(gè)線程的運(yùn)行,為完成端口提供服務(wù),避免過于頻繁的線程“場(chǎng)景”(即線程上下文)切換。

若將該參數(shù)設(shè)為0,表明系統(tǒng)內(nèi)安裝了多少個(gè)處理器,便允許同時(shí)運(yùn)行多少個(gè)工線程!可用下述代碼創(chuàng)建一個(gè)I/O完成端口:

HANDLECompletionPort=CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,0);

★1、工線程與完成端口

成功創(chuàng)建一個(gè)完成端口后,便可開始將套接字句柄與其關(guān)聯(lián)到一起。但在關(guān)聯(lián)套接字之前,首先必須創(chuàng)建一個(gè)或多個(gè)“工線程”,

以便在I/O請(qǐng)求投遞給完成端口后,為完成端口提供服務(wù)。在這個(gè)時(shí)候,大家或許會(huì)覺得奇怪,到底應(yīng)創(chuàng)建多少個(gè)線程,以便為完成端口提供服務(wù)呢?

在此,要記住的一點(diǎn),我們調(diào)用CreateIoComletionPort時(shí)指定的并發(fā)線程數(shù)量,與打算創(chuàng)建的工線程數(shù)量相比,它們代表的不是同一件事情。

CreateIoCompletionPort函數(shù)的NumberOfConcurrentThreads參數(shù)明確指示系統(tǒng):

在一個(gè)完成端口上,一次只允許n個(gè)工線程運(yùn)行。假如在完成端口上創(chuàng)建的工線程數(shù)量超出n個(gè),那么在同一時(shí)刻,最多只允許n個(gè)線程運(yùn)行。

但實(shí)際上,在一段較短的時(shí)間內(nèi),系統(tǒng)有可能超過這個(gè)值,但很快便會(huì)把它減少至事先在CreateIoCompletionPort函數(shù)中設(shè)定的值。

那么,為何實(shí)際創(chuàng)建的工線程數(shù)量有時(shí)要比CreateIoCompletionPort函數(shù)設(shè)定的

多一些呢?這樣做有必要嗎?

這主要取決于應(yīng)用程序的總體設(shè)計(jì)情況。假定我們的某個(gè)工線程調(diào)用了一個(gè)函數(shù),比如Sleep或WaitForSingleObject,

進(jìn)入了暫停(鎖定或掛起)狀態(tài),那么允許另一個(gè)線程代替它的位置。換言之,我們

希望隨時(shí)都能執(zhí)行盡可能多的線程;

當(dāng)然,最大的線程數(shù)量是事先在CreateIoCompletonPort調(diào)用里設(shè)定好的。這樣一來,假如事先預(yù)計(jì)到自己的線程有可能暫時(shí)處于停頓狀態(tài),

那么最好能夠創(chuàng)建比CreateIoCompletonPort的NumberOfConcurrentThreads參數(shù)的值多的線程,以便到時(shí)候充分發(fā)揮系統(tǒng)的潛力。

==========================================

每一種模型適用于一種特定的應(yīng)用場(chǎng)景。程序員應(yīng)該對(duì)自己的應(yīng)用需求非常明確,

綜合考慮到程序的擴(kuò)展性和可移植性等因素,作出自己的選擇。

==============================================

█“完成端口”模型是迄今為止最復(fù)雜的一種I/O模型。但是,若一個(gè)應(yīng)用程序同時(shí)

需要管理很多的套接字,

那么采用這種模型,往往可以達(dá)到最佳的系統(tǒng)性能!但缺點(diǎn)是,該模型只適用于WindowsNT和Windows2000以上版本的操作系統(tǒng)。

█因其設(shè)計(jì)的復(fù)雜性,只有在你的應(yīng)用程序需要同時(shí)管理數(shù)百乃至上千個(gè)套接字的時(shí)候,而且希望隨著系統(tǒng)內(nèi)安裝的CPU數(shù)量的增多,

應(yīng)用程序的性能也可以線性提升,才應(yīng)考慮采用“完成端口”模型。

█從本質(zhì)上說,完成端口模型要求我們創(chuàng)建一個(gè)Win32完成端口對(duì)象,通過指定數(shù)量的線程,

對(duì)重疊I/O請(qǐng)求進(jìn)行管理,以便為已經(jīng)完成的重疊I/O請(qǐng)求提供服務(wù)。

█※※※大家可以這樣理解,一個(gè)完成端口其實(shí)就是一個(gè)完成I/O的通知隊(duì)列,由操作系統(tǒng)把已經(jīng)完成的重疊I/O請(qǐng)求的通知放入這個(gè)隊(duì)列中。

當(dāng)某項(xiàng)I/O操作一旦完成,某個(gè)可以對(duì)該操作結(jié)果進(jìn)行處理的工線程就會(huì)收到一則通知,工線程再去做一些其他的善后工作,

比如:將收到的數(shù)據(jù)進(jìn)行顯示,等等。而套接字在被創(chuàng)建后,可以在任何時(shí)候與某個(gè)完成端口進(jìn)行關(guān)聯(lián)?!?/p>

通常情況下,我們會(huì)在應(yīng)用程序中創(chuàng)建一定數(shù)量的工線程來處理這些通知。線程數(shù)量取決于應(yīng)用程序的特定需要。理想的情況是,線程數(shù)量等于處理器的數(shù)量,不過這也要求任何線程都不應(yīng)該執(zhí)行諸如同步讀寫、等待事件通知等阻塞型的操作,以免線程阻塞。每個(gè)線程都將分到一定的CPU時(shí)間,在此期間該線程可以運(yùn)行,然后另一個(gè)線程將分到一個(gè)時(shí)間片并開始執(zhí)行。如果某個(gè)線程執(zhí)行了阻塞型的操作,操作系統(tǒng)將剝奪其未使用的剩余時(shí)間片并讓其它線程開始執(zhí)行。也就是說,前一個(gè)線程沒有充分使用其時(shí)間片,當(dāng)發(fā)生這樣的情況時(shí),應(yīng)用程序應(yīng)該準(zhǔn)備其它線程來充分利用這些時(shí)間片。

█使用這種模型之前,首先要?jiǎng)?chuàng)建一個(gè)I/O完成端口對(duì)象,用它面向任意數(shù)量的套接字句柄,管理多個(gè)I/O請(qǐng)求。

要做到這一點(diǎn),需要調(diào)用CreateCompletionPort函數(shù),其定義如下:

HANDLEWINAPICreateIoCompletionPort(

__inHANDLEFileHandle,

__inHANDLEExistingCompletionPort,

__inULONG_PTRCompletionKey,

__inDWORDNumberOfConcurrentThreads

);

要注意該函數(shù)有兩個(gè)功能:

●用于創(chuàng)建一個(gè)完成端口對(duì)象;

●將一個(gè)句柄同完成端口對(duì)象關(guān)聯(lián)到一起。

如果僅僅為了創(chuàng)建一個(gè)完成端口對(duì)象,唯一注意的參數(shù)便是NumberOfConcurrentThreads(并發(fā)線程的數(shù)量),前面三個(gè)參數(shù)可忽略。

NumberOfConcurrentThreads參數(shù)的特殊之處在于,它定義了在一個(gè)完成端口上,同時(shí)允許執(zhí)行的線程數(shù)量。

理想情況下,我們希望每個(gè)處理器各自負(fù)責(zé)一個(gè)線程的運(yùn)行,為完成端口提供服務(wù),避免過于頻繁的線程“場(chǎng)景”(即線程上下文)切換。

若將該參數(shù)設(shè)為0,表明系統(tǒng)內(nèi)安裝了多少個(gè)處理器,便允許同時(shí)運(yùn)行多少個(gè)工線程!可用下述代碼創(chuàng)建一個(gè)I/O完成端口:

HANDLECompletionPort=CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,0);

★1、工線程與完成端口

成功創(chuàng)建一個(gè)完成端口后,便可開始將套接字句柄與其關(guān)聯(lián)到一起。但在關(guān)聯(lián)套接字之前,首先必須創(chuàng)建一個(gè)或多個(gè)“工線程”,

以便在I/O請(qǐng)求投遞給完成端口后,為完成端口提供服務(wù)。在這個(gè)時(shí)候,大家或許會(huì)覺得奇怪,到底應(yīng)創(chuàng)建多少個(gè)線程,以便為完成端口提供服務(wù)呢?

在此,要記住的一點(diǎn),我們調(diào)用CreateIoComletionPort時(shí)指定的并發(fā)線程數(shù)量,與打算創(chuàng)建的工線程數(shù)量相比,它們代表的不是同一件事情。

CreateIoCompletionPort函數(shù)的NumberOfConcurrentThreads參數(shù)明確指示系統(tǒng):

在一個(gè)完成端口上,一次只允許n個(gè)工線程運(yùn)行。假如在完成端口上創(chuàng)建的工線程數(shù)量超出n個(gè),那么在同一時(shí)刻,最多只允許n個(gè)線程運(yùn)行。

但實(shí)際上,在一段較短的時(shí)間內(nèi),系統(tǒng)有可能超過這個(gè)值,但很快便會(huì)把它減少至事

先在CreateIoCompletionPort函數(shù)中設(shè)定的值。

那么,為何實(shí)際創(chuàng)建的工線程數(shù)量有時(shí)要比CreateIoCompletionPort函數(shù)設(shè)定的

多一些呢?這樣做有必要嗎?

這主要取決于應(yīng)用程序的總體設(shè)計(jì)情況。假定我們的某個(gè)工線程調(diào)用了一個(gè)函數(shù),比如Sleep或WaitForSingleObject,

進(jìn)入了暫停(鎖定或掛起)狀態(tài),那么允許另一個(gè)線程代替它的位置。換言之,我們

希望隨時(shí)都能執(zhí)行盡可能多的線程;

當(dāng)然,最大的線程數(shù)量是事先在CreateIoCompletonPort調(diào)用里設(shè)定好的。這樣一來,假如事先預(yù)計(jì)到自己的線程有可能暫時(shí)處于停頓狀態(tài),

那么最好能夠創(chuàng)建比CreateIoCompletonPort的NumberOfConcurrentThreads參數(shù)的值多的線程,以便到時(shí)候充分發(fā)揮系統(tǒng)的潛力。

==========================================

一旦在完成端口上擁有足夠多的工線程來為I/O請(qǐng)求提供服務(wù),便可著手將套接

字句柄同完成端口關(guān)聯(lián)到一起。

這要求我們?cè)谝粋€(gè)現(xiàn)有的完成端口上,調(diào)用CreateIoCompletionPort函數(shù),同時(shí)為前

三個(gè)參數(shù)—FileHandle,ExistingCompletionPort和CompletionKey—提供套接字的信息。

●FileHandle參數(shù)指定一個(gè)要同完成端口關(guān)聯(lián)在一起的套接字句柄;

●ExistingCompletionPort參數(shù)指定的是一個(gè)現(xiàn)有的完成端口;

●CompletionKey(完成鍵)參數(shù)指定與某個(gè)套接字句柄關(guān)聯(lián)在一起的“單句柄數(shù)據(jù)”,可將其作為指向一個(gè)數(shù)據(jù)結(jié)構(gòu)的指針,

在此數(shù)據(jù)結(jié)構(gòu)中,同時(shí)包含了套接字的句柄,以及與套接字有關(guān)的其他信息,如IP地址等。為完成端口提供服務(wù)的線程函數(shù)可通過這個(gè)參數(shù),取得與套接字句柄有關(guān)的信息。

根據(jù)目前,首先來構(gòu)建一個(gè)基本的應(yīng)用程序框架。下面的程序清單向

大家闡述了如何使用完成端口模型,來開發(fā)一個(gè)服務(wù)器應(yīng)用。在這個(gè)程序中,

我們按照以下步驟進(jìn)行:

1)創(chuàng)建一個(gè)完成端口,第四個(gè)參數(shù)保持為0,指定在完成端口上,每個(gè)處理器一次只允許執(zhí)行一個(gè)工線程;

2)判斷系統(tǒng)內(nèi)到底安裝了多少個(gè)處理器;

3)創(chuàng)建工線程,根據(jù)步驟2)得到的處理器信息,在完成端口上,為已完成的I/O請(qǐng)求提供服務(wù),在這個(gè)簡(jiǎn)單的例子中,我們?yōu)槊總€(gè)處理器都只創(chuàng)建一個(gè)工線程。

這是由于事先已預(yù)計(jì)到,到時(shí)不會(huì)有任何線程進(jìn)入“掛起”狀態(tài),造成由于線程數(shù)量的不足,而使處理器空閑的局面(沒有足夠的線程可供執(zhí)行)。

調(diào)用CreateThread函數(shù)時(shí),必須同時(shí)提供一個(gè)工例程,由線程在創(chuàng)建好執(zhí)行;

4)準(zhǔn)備好一個(gè)監(jiān)聽套接字,在端口9527上監(jiān)聽進(jìn)入的連接請(qǐng)求;

5)使用accept函數(shù),接受進(jìn)入的連接請(qǐng)求;

6)創(chuàng)建一個(gè)數(shù)據(jù)結(jié)構(gòu),用于容納“單句柄數(shù)據(jù)”,同時(shí)在結(jié)構(gòu)中存入接受的套接字句柄;

7)調(diào)用CreateIoCompletionPort函數(shù),將從accept返回的新套接字句柄同完成端口關(guān)聯(lián)到一起,

通過完成鍵(CompletionKey)參數(shù),將單句柄數(shù)據(jù)結(jié)構(gòu)傳遞給CreateIoCompletionPort函數(shù);

8)開始在已接受的連接上進(jìn)行I/O操作,在此,我們希望通過重疊I/O機(jī)制,在新建的套接字上投遞一個(gè)或多個(gè)異步WSARecv或WSASend請(qǐng)求。

這些I/O請(qǐng)求完成后,一個(gè)工線程會(huì)為I/O請(qǐng)求提供服務(wù),同時(shí)繼續(xù)處理未來的其他I/O請(qǐng)求,

稍后便會(huì)在步驟3)指定的工例程中,體驗(yàn)到這一點(diǎn);

9)重復(fù)步驟5)~8),直至服務(wù)器中止。

代碼如下:

HANDLECompletionPort;

WSADATAwsd;

SYSTEM_INFOSystemInfo;

SOCKADDR_INInternetAddr;

SOCKETListen;

inti;

typedefstruct_PER_HANDLE_DATA

{

SOCKETSocket;

SOCKADDR_STORAGEClientAddr;

//Otherinformationusefultobeassociatedwiththehandle}PER_HANDLE_DATA,*LPPER_HANDLE_DATA;

//LoadWinsock

StartWinsock(MAKEWORD(2,2),

//Step1:

//創(chuàng)建一個(gè)完成端口

CompletionPort=CreateIoCompletionPort(

INVALID_HANDLE_VALUE,NULL,0,0);

//Step2:

//判斷系統(tǒng)內(nèi)到底安裝了多少個(gè)處理器

GetSystemInfo(

//Step3:

//根據(jù)處理器的數(shù)量創(chuàng)建工線程

for(i=0;iSocket=Accept;

memcpy(

//Step7:

//調(diào)用CreateIoCompletionPort函數(shù),將從accept返回的新套接字句柄同完成端口關(guān)聯(lián)到一起

CreateIoCompletionPort((HANDLE)Accept,

CompletionPort,(DWORD)PerHandleData,0);

//Step8:

//開始在已接受的連接上進(jìn)行I/O操作

WSARecv(...);

}

DWORDWINAPIServerWorkerThread(LPVOIDlpParam)

{

//Therequirementsfortheworkerthreadwillbe

//discussedlater.

return0;

}

★2、完成端口和重疊I/O(工線程要做的事情)

將套接字句柄與一個(gè)完成端口關(guān)聯(lián)在一起后,便可投遞發(fā)送與接收請(qǐng)求,開始對(duì)I/O請(qǐng)求的處理。

接下來,可開始依賴完成端口,來接收有關(guān)I/O操作完成情況的通知。

從本質(zhì)上說,完成端口模型利用了Win32重疊I/O機(jī)制。在這種機(jī)制中,象WSASend和WSARecv這樣的WinsockAPI調(diào)用會(huì)立即返回。

此時(shí),需要由我們的應(yīng)用程序負(fù)責(zé)在以后的某個(gè)時(shí)間,通過一個(gè)OVERLAPPED結(jié)構(gòu),來接收之前調(diào)用請(qǐng)求的結(jié)果。

在完成端口模型中,要想做到這一點(diǎn),需要使用GetQueuedCompletionStatus(獲取

排隊(duì)完成狀態(tài))函數(shù),

讓一個(gè)或多個(gè)工線程在完成端口上等待I/O請(qǐng)求完成的通知。該函數(shù)的定義如下:BOOLWINAPIGetQueuedCompletionStatus(

__inHANDLECompletionPort,

__outLPDWORDlpNumberOfBytes,

__outPULONG_PTRlpCompletionKey,

__outLPOVERLAPPED*lpOverlapped,

__inDWORDdwMilliseconds

);

●CompletionPort參數(shù)對(duì)應(yīng)于要在上面等待的完成端口;

●lpNumberOfBytes參數(shù)負(fù)責(zé)在完成了一次I/O操作后(如:WSASend或WSARecv),接收實(shí)際傳輸?shù)淖止?jié)數(shù)。

●lpCompletionKey參數(shù)為原先傳遞給CreateIoCompletionPort函數(shù)第三個(gè)參數(shù)“單句柄數(shù)據(jù)”,如我們?cè)缦人?,大家最好將套接字句柄保存在這個(gè)“鍵”(Key)中。

●lpOverlapped參數(shù)用于接收完成I/O操作的重疊結(jié)果。這實(shí)際是一個(gè)相當(dāng)重要的參數(shù),因?yàn)榭捎盟@取每個(gè)I/O操作的數(shù)據(jù)。

●dwMilliseconds參數(shù)用于指定希望等待一個(gè)完成數(shù)據(jù)包在完成端口上出現(xiàn)的時(shí)間,即,超時(shí)時(shí)間。假如將其設(shè)為INFINITE,會(huì)一直等待下去。

★3、“單句柄數(shù)據(jù)”和單I/O操作數(shù)據(jù)

一個(gè)工線程從GetQueuedCompletionStatus函數(shù)接收到I/O完成通知后,在lpCompletionKey和lpOverlapped參數(shù)中,

會(huì)包含一些重要的套接字信息。利用這些信息,可通過完成端口,繼續(xù)在一個(gè)套接字

上進(jìn)行其他的處理。

通過這些參數(shù),可獲得兩方面重要的套接字?jǐn)?shù)據(jù):“單句柄數(shù)據(jù)”以及單I/O操作數(shù)據(jù)。

其中,lpCompletionKey參數(shù)包含了“單句柄數(shù)據(jù)”,因?yàn)樵谝粋€(gè)套接字首次與完成端口關(guān)聯(lián)到一起的時(shí)候,

那些數(shù)據(jù)便與一個(gè)特定的套接字句柄對(duì)應(yīng)起來了。這些數(shù)據(jù)正是我們?cè)谡{(diào)用CreateIoCompletionPort函數(shù)時(shí)候,通過CompletionKey參數(shù)傳遞的。

通常情況下,應(yīng)用程序會(huì)將與I/O請(qǐng)求有關(guān)的套接字句柄及其他的一些相關(guān)信息保存在這里;

lpOverlapped參數(shù)則包含了一個(gè)OVERLAPPED結(jié)構(gòu),在它后面跟隨“單I/O操作數(shù)據(jù)”。

單I/O操作數(shù)據(jù)可以是追加到一個(gè)OVERLAPPED結(jié)構(gòu)末尾的、任意數(shù)量的字節(jié)。

假如一個(gè)函數(shù)要求用到一個(gè)OVERLAPPED結(jié)構(gòu),我們便必須將這樣的一個(gè)結(jié)構(gòu)傳遞進(jìn)去,以滿足它的要求。

要想做到這一點(diǎn),一個(gè)簡(jiǎn)單的方法是定義一個(gè)結(jié)構(gòu),然后將OVERLAPPED結(jié)構(gòu)作為新結(jié)構(gòu)的第一個(gè)元素使用。

舉個(gè)例子來說,可定義下述數(shù)據(jù)結(jié)構(gòu),實(shí)現(xiàn)對(duì)單I/O操作數(shù)據(jù)的管理:

typedefstruct

{

OVERLAPPEDOverlapped;

WSABUFDataBuf;

charszBuffer[DATA_BUF_SIZE];

intOperationType;

}PER_IO_OPERATION_DATA;

該結(jié)構(gòu)演示了通常與I/O操作關(guān)聯(lián)的一些重要的數(shù)據(jù)元素,比如剛才完成的那個(gè)I/O操作的類型(發(fā)送或接收請(qǐng)求),用OperationType字段表示,

同時(shí),用于已完成I/O操作數(shù)據(jù)的緩沖區(qū)szBuffer也是非常有用的。如果想調(diào)用一個(gè)WinsockAPI函數(shù)(如:WSASend、WSARecv),要為其分配一個(gè)OVERLAPPED結(jié)構(gòu),

這時(shí),就可以將我們的結(jié)構(gòu)強(qiáng)制轉(zhuǎn)換成一個(gè)OVERLAPPED指針,或者從結(jié)構(gòu)中將OVERLAPPED元素的地址取出來。如下例所示:

PER_IO_OPERATION_DATAPerIoData;

……

//可以這樣調(diào)用:

WSARecv(socket,...,(OVERLAPPED*)

//也可以這樣調(diào)用:

WSARecv(socket,...,

在工作線程的后面部分,等GetQueuedCompletionStatus函數(shù)返回了一個(gè)重疊結(jié)構(gòu)(和完成鍵)后,

便可通過OperationType成員,看出到底是哪個(gè)操作投遞到了這個(gè)句柄之上(只需將

返回的重疊結(jié)強(qiáng)制轉(zhuǎn)換為自己的PER_IO_OPERATION_DATA結(jié)構(gòu))。

對(duì)單I/O操作數(shù)據(jù)來說,它最大的一個(gè)優(yōu)點(diǎn)便是允許我們?cè)谕粋€(gè)句柄上,同時(shí)管理

多個(gè)I/O操作(讀/寫,多個(gè)讀,多個(gè)寫,等等)。

DWORDWINAPIServerWorkerThread(LPVOIDCompletionPortID)

{

HANDLECompletionPort=(HANDLE)CompletionPortID;

DWORDBytesTransferred;

LPOVERLAPPEDOverlapped;

LPPER_HANDLE_DATAPerHandleData;

LPPER_IO_DATAPerIoData;

DWORDSendBytes,RecvBytes;

DWORDFlags;

while(TRUE)

{

//WaitforI/Otocompleteonanysocket

//associatedwiththecompletionport

ret=GetQueuedCompletionStatus(CompletionPort,

//Firstchecktoseeifanerrorhasoccurred

//onthesocket;ifso,closethe

//socketandcleanuptheper-handledata

//andper-I/Ooperationdataassociatedwith

//thesocket

if(BytesTransferred==0

GlobalFree(PerHandleData);

GlobalFree(PerIoData);

continue;

}

//ServicethecompletedI/Orequest.Youcan

//determinewhichI/Orequesthasjust

//completedbylookingattheOperationType

//fieldcontainedintheper-I/Ooperationdata.

if(PerIoData->OperationType==RECV_POSTED){

//Dosomethingwiththereceiveddata

//inPerIoData->Buffer

}

//PostanotherWSASendorWSARecvoperation.//Asanexample,wewillpostanotherWSARecv()//I/Ooperation.

Flags=0;

//Setuptheper-I/Ooperationdataforthenext//overlappedcall

ZeroMemory(

PerIoData->DataBuf.len=DATA_BUFSIZE;

PerIoData->DataBuf.buf=PerIoData->Buffer;

PerIoData->OperationType=RECV_POSTED;

WSARecv(PerHandleData->Socket,

}

★4、正確地關(guān)閉I/O完成端口

如何正確地關(guān)閉I/O完成端口,特別是同時(shí)運(yùn)行了一個(gè)或多個(gè)線程,在幾個(gè)不同的套

接字上執(zhí)行I/O操作的時(shí)候。

要避免的一個(gè)重要問題是在進(jìn)行重疊I/O操作的同時(shí),強(qiáng)行釋放一個(gè)OVERLAPPED結(jié)構(gòu)。

要想避免出現(xiàn)這種情況,最好的辦法是針對(duì)每個(gè)套接字句柄,調(diào)用closesocket函數(shù),任何尚未進(jìn)行的重疊I/O操作都會(huì)完成。一旦所有套接字句柄都已關(guān)閉,

便需在完成端口上,終止所有工線程的運(yùn)行。要想做到這一點(diǎn),需要使用PostQueuedCompletionStatus函數(shù),向每個(gè)工線程都發(fā)送一個(gè)特殊的完成數(shù)據(jù)包。

該函數(shù)會(huì)指示每個(gè)線程都“立即結(jié)束并退出”。下面是PostQueuedCompletionStatus函數(shù)的定義:

BOOLWINAPIPostQueuedCompletionStatus(

__inHANDLECompletionPort,

__inDWORDdwNumberOfBytesTransferred,

__inULONG_PTRdwCompletionKey,

__inLPOVERLAPPEDlpOverlapped

);

●CompletionPort參數(shù)指定想向其發(fā)送一個(gè)完成數(shù)據(jù)包的完成端口對(duì)象;

●而就dwNumberOfBytesTransferred、dwCompletionKey和lpOverlapped三個(gè)參數(shù)

來說,每一個(gè)都允許我們指定一個(gè)值,

直接傳遞給GetQueuedCompletionStatus函數(shù)中對(duì)應(yīng)的參數(shù)。這樣一來,一個(gè)工線程收到傳遞過來的三個(gè)GetQueuedCompletionStatus函數(shù)參數(shù)后,

便可根據(jù)由這三個(gè)參數(shù)的某一個(gè)設(shè)置的特殊值,決定何時(shí)或者應(yīng)該怎樣退出。

例如,可用dwCompletionPort參數(shù)傳遞0值,而一個(gè)工線程會(huì)將其解釋成中止

指令。

一旦所有工線程都已關(guān)閉,便可使用CloseHandle函數(shù),關(guān)閉完成端口,最終安

全退出程序。

==========================================

1、線程池的基本原理

在傳統(tǒng)服務(wù)器架構(gòu)中,常常是有一個(gè)總的監(jiān)聽線程監(jiān)聽有沒有新的用戶連接服務(wù)器,每當(dāng)有一個(gè)新的用戶連接進(jìn)入,服務(wù)器端就開啟一個(gè)新的線程去處理這個(gè)用戶的請(qǐng)求,與其進(jìn)行數(shù)據(jù)的收發(fā)。

這個(gè)線程只服務(wù)于這個(gè)用戶,當(dāng)用戶與服務(wù)器端關(guān)閉連接以后,服務(wù)器端才銷毀這個(gè)線程。然而頻繁地開辟與銷毀線程極大地占用了系統(tǒng)的資源。而且在大量用戶的情況下,少則1000,多則上萬(wàn),系統(tǒng)為了開辟和銷毀線程將浪費(fèi)大量的時(shí)間和資源。

線程池技術(shù)很好的解決了這個(gè)問題,它的基本思想就是在程序開始時(shí)就在內(nèi)存中開辟一些線程,當(dāng)有新的客戶請(qǐng)求到達(dá)時(shí),

不是新創(chuàng)建一個(gè)線程為其服務(wù),而是從“池子”中選擇一個(gè)空閑的線程為新的客戶請(qǐng)求服務(wù),服務(wù)完畢后,線程不是退出,而是進(jìn)入空閑線程池中。

通過對(duì)多個(gè)任務(wù)重用已經(jīng)存在的線程對(duì)象,降低了對(duì)線程對(duì)象創(chuàng)建和銷毀的開銷。當(dāng)客戶請(qǐng)求時(shí),線程對(duì)象已經(jīng)存在,可以提高請(qǐng)求的響應(yīng)時(shí)間,從而整體地提高了系統(tǒng)服務(wù)的表現(xiàn)。

2、線程池的實(shí)現(xiàn)

如果大家有時(shí)間的話,可以自己實(shí)現(xiàn)一個(gè)高效的線程池,當(dāng)然網(wǎng)上也有很多版本的線程池源代碼,大家可

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說明,都需要本地電腦安裝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ì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論