版權(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ì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 《酒店新員工培訓(xùn)》課件
- 《教育本質(zhì)》課件
- 《詞類句子成分》課件
- 急性風(fēng)濕熱的健康宣教
- 兒童牙病的健康宣教
- 垂體性閉經(jīng)的健康宣教
- 孕期水樣分泌物的健康宣教
- 《例解決問題》課件
- 武漢大學(xué)金融工程學(xué)課件-金融工程
- 腎上腺髓質(zhì)增生的臨床護(hù)理
- 2022山東能源集團(tuán)中級(jí)人才庫(kù)選拔上岸筆試歷年難、易錯(cuò)點(diǎn)考題附帶參考答案與詳解
- 音樂學(xué)專業(yè)藝術(shù)實(shí)踐調(diào)研報(bào)告范文
- 臀位助產(chǎn)術(shù)課件
- 村集體經(jīng)濟(jì)組織收支預(yù)算編制
- 2022-2023年共青團(tuán)應(yīng)知應(yīng)會(huì)知識(shí)題庫(kù)附答案(新版)
- 車輛維修安全保障措施
- 《道德經(jīng)》與管理智慧知到章節(jié)答案智慧樹2023年華僑大學(xué)
- 這么寫網(wǎng)約車事故索賠誤工費(fèi)的起訴狀更容易勝訴
- GB/T 38119-2019邵氏硬度計(jì)的檢驗(yàn)
- GB/T 31856-2015廢氯氣處理處置規(guī)范
- 珠寶領(lǐng)域:周大生企業(yè)組織結(jié)構(gòu)及部門職責(zé)
評(píng)論
0/150
提交評(píng)論