• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            posts - 34,comments - 2,trackbacks - 0

            為什么要使用線程池:
            創(chuàng)建多線程應(yīng)用程序是非常困難的。需要會(huì)面臨兩個(gè)大問(wèn)題。
            一個(gè)是要對(duì)線程的創(chuàng)建和撤消進(jìn)行管理,另一個(gè)是要對(duì)線程對(duì)資源的訪問(wèn)實(shí)施同步 。 
            線程池(thread pool),允許有多個(gè)線程同時(shí)存在,并發(fā)執(zhí)行,并且這些線程受到統(tǒng)一管理。

              在Windows Vista中,提供了全新的線程池機(jī)制,一般這些線程池中的線程的創(chuàng)建的銷(xiāo)毀是由操作系統(tǒng)自動(dòng)完成的。

              Windows Vista 中重新設(shè)計(jì)了線程池,提供了一組新的線程池API。因此,本篇討論的僅僅在Windows Vista系統(tǒng),或其以上的Windows版本中有效。

              當(dāng)一個(gè)進(jìn)程創(chuàng)建之后,它并不與線程池關(guān)聯(lián)。一旦新的線程池API函數(shù)被呼叫之后,系統(tǒng)就為該進(jìn)程創(chuàng)建內(nèi)核資源,并且有些資源直到進(jìn)程結(jié)束才釋放。因此,在使用線程池的時(shí)候,線程、其他內(nèi)核對(duì)象、內(nèi)部數(shù)據(jù)結(jié)構(gòu)被分配給進(jìn)程,因此要考慮線程池是否確實(shí)必要。

              線程池機(jī)制有4種功能:

            1、調(diào)用一個(gè)異步函數(shù)

            2、定時(shí)地調(diào)用一個(gè)函數(shù)

            3、當(dāng)一個(gè)內(nèi)核對(duì)象被通知的時(shí)候調(diào)用一個(gè)函數(shù)

            4、當(dāng)一個(gè)異步I/O請(qǐng)求完成的時(shí)候調(diào)用一個(gè)函數(shù)

             

              而這4個(gè)功能都和線程池中的“工作項(xiàng)”息息相關(guān)。可以把“工作項(xiàng)”看作是一個(gè)特定的工作記錄,記錄著異步函數(shù),線程池定時(shí)器信息,線程池等待對(duì)象信息,線程池I/O對(duì)象,而這4個(gè)對(duì)象就是實(shí)現(xiàn)上述4個(gè)功能的要素。

             

            調(diào)用一個(gè)異步函數(shù)

              首先來(lái)討論一下第1種功能:調(diào)用一個(gè)異步函數(shù)。其基本步驟可以有兩種:

              第一種:

            1、定義一個(gè)給定格式的異步函數(shù)

            2、提交這個(gè)異步函數(shù)給線程池

             

              第二種:

            1、定義一個(gè)給定格式的異步函數(shù)

            2、創(chuàng)建一個(gè)“工作項(xiàng)”,該工作項(xiàng)與異步函數(shù)、異步函數(shù)參數(shù)關(guān)聯(lián)

            3、將這個(gè)工作項(xiàng)提交給線程池

            4、關(guān)閉創(chuàng)建的“工作項(xiàng)”

             

              為了在線程池中調(diào)用一個(gè)異步函數(shù),該異步函數(shù)的定義如下(第1個(gè)參數(shù)pInstance暫不討論,可以簡(jiǎn)單地傳遞NULL,下同):

            VOID NTAPI SimpleCallback(          //函數(shù)名可以任意
                 PTP_CALLBACK_INSTANCE pInstance,
                 PVOID pvContext);     
            //函數(shù)參數(shù)

             

              然后,你可以提交一個(gè)請(qǐng)求給線程池,讓其中的一個(gè)線程執(zhí)行這個(gè)函數(shù):

            BOOL TrySubmitThreadpoolCallback(
               PTP_SIMPLE_CALLBACK pfnCallback,   
            //按上面格式定義的異步函數(shù)的指針
               PVOID pvContext,          //傳遞給異步函數(shù)的參數(shù)
               PTP_CALLBACK_ENVIRON pcbe);

             

              TrySubmitThreadpoolCallback 函數(shù)在線程池隊(duì)列中加入一個(gè)“工作項(xiàng)”(work item),如果成功返回TRUE,否則返回FLASE。pcbe參數(shù)下面會(huì)介紹,你可以簡(jiǎn)單地傳遞NULL給這個(gè)參數(shù)(下同)。

              你不必調(diào)用CreateThread來(lái)創(chuàng)建線程,當(dāng)進(jìn)程內(nèi)調(diào)用TrySubmitThreadpoolCallback函數(shù)的時(shí)候,系統(tǒng)會(huì)自動(dòng)地給你的進(jìn)程創(chuàng)建線程池,然后在該線程池隊(duì)列中隊(duì)列中加入一個(gè)“工作項(xiàng)”,并讓其中的一個(gè)線程來(lái)執(zhí)行你定義的異步函數(shù)。當(dāng)異步函數(shù)執(zhí)行完畢后,該線程不會(huì)被銷(xiāo)毀,而是進(jìn)入線程池等待另一個(gè)“工作項(xiàng)”的到來(lái)。線程池中的線程是回收利用的,并不是不斷創(chuàng)建和銷(xiāo)毀的,這樣提高了性能。同時(shí),這個(gè)線程池如果覺(jué)得自己的線程太多的話,就自動(dòng)地銷(xiāo)毀一些線程,可以讓性能達(dá)到最佳。

              你可以使用CreateThreadpoolWork函數(shù)來(lái)創(chuàng)建一個(gè)“工作項(xiàng)”:

            PTP_WORK CreateThreadpoolWork(
               PTP_WORK_CALLBACK pfnWorkHandler,     
            //異步函數(shù)指針
               PVOID pvContext,                      //異步函數(shù)參數(shù)
               PTP_CALLBACK_ENVIRON pcbe);

             

              該函數(shù)接受一個(gè)異步函數(shù)的指針和這個(gè)異步函數(shù)的參數(shù),并創(chuàng)建一個(gè)用戶模式的數(shù)據(jù)結(jié)構(gòu)來(lái)保存對(duì)應(yīng)的3個(gè)參數(shù)的數(shù)據(jù),同時(shí)返回一個(gè)指向這個(gè)數(shù)據(jù)結(jié)構(gòu)的指針,可以理解為“工作項(xiàng)”指針。

              其中,pfnWordHandler 函數(shù)是一個(gè)異步函數(shù)的指針,這個(gè)異步函數(shù)會(huì)被線程池中某個(gè)線程調(diào)用,該異步函數(shù)定義如下:

            VOID CALLBACK WorkCallback(     //函數(shù)名可以任意
               PTP_CALLBACK_INSTANCE Instance,
               PVOID Context,      
            //異步函數(shù)參數(shù),由CreateThreadpoolWork函數(shù)指定
               PTP_WORK Work);     //線程池“工作項(xiàng)”指針

             

              當(dāng)你想將一個(gè)創(chuàng)建了的工作項(xiàng)提交給線程池,可以使用SubmitThreadpoolWork函數(shù):

            VOID SubmitThreadpoolWork(PTP_WORK pWork);     //參數(shù)是工作項(xiàng)指針

             

              如果多次調(diào)用該函數(shù)向一個(gè)線程池提交同一個(gè)工作項(xiàng),那么異步函數(shù)會(huì)被調(diào)用多次,而每次的參數(shù)都是同樣一個(gè)值。

              如果有另一個(gè)線程想要取消提交的工作項(xiàng),或者掛起自己等待工作項(xiàng)完成,可以使用這個(gè)函數(shù):

            VOID WaitForThreadpoolWorkCallbacks(
               PTP_WORK pWork,     
            //工作項(xiàng)指針
               BOOL bCancelPendingCallbacks);  //是否取消該工作項(xiàng)

             

              pWork 函數(shù)是一個(gè)工作項(xiàng)指針,由函數(shù)CreateThreadpoolWork創(chuàng)建并返回。如果該工作項(xiàng)沒(méi)有被提交,則該函數(shù)馬上返回,不做任何工作。

              如果傳遞TRUE給參數(shù)bCancelPendingCallbacks,WaitForThreadpoolWorkCallbacks函數(shù)將試圖取消這個(gè)先前提交的工作項(xiàng)。如果這個(gè)工作項(xiàng)正在被處理,那么這個(gè)處理不會(huì)被打斷,該函數(shù)會(huì)等待直到工作項(xiàng)結(jié)束才返回。如果這個(gè)工作項(xiàng)被提交,但是目前不在處理,那么該函數(shù)就會(huì)立即取消該工作項(xiàng)并理解返回,那么這個(gè)工作項(xiàng)的異步函數(shù)就不會(huì)被調(diào)用了。

              如果傳遞FALSE給傳遞bCancelPendingCallbacks,WaitForThreadpoolWorkCallbacks函數(shù)將掛起這個(gè)調(diào)用它的線程,直到指定的工作項(xiàng)完成,而線程池中執(zhí)行這個(gè)工作項(xiàng)的線程在完成處理工作項(xiàng)之后返回線程池,繼續(xù)處理下一個(gè)工作項(xiàng)。

              如果傳遞給WaitForThreadpoolWorkCallbacks函數(shù)的第一個(gè)參數(shù)的工作項(xiàng)指針被提交給線程池多次,也就是說(shuō)多個(gè)工作項(xiàng)使用同一個(gè)工作項(xiàng)指針,如果第2個(gè)參數(shù)為FALSE,那么WaitForThreadpoolWorkCallbacks將等到這個(gè)工作項(xiàng)指針代表的所有工作項(xiàng)處理完成才返回。如果傳遞TRUE給第2個(gè)參數(shù),WaitForThreadpoolWorkCallbacks將等待,只要當(dāng)前正在執(zhí)行的工作項(xiàng)結(jié)束就返回。

              當(dāng)你不要使用工作項(xiàng)的時(shí)候,使用CloseThreadpoolWork函數(shù)關(guān)閉之。 

            VOID CloseThreadpoolWork(PTP_WORK pwk);

             

            定時(shí)調(diào)用一個(gè)函數(shù)

              這是Windows線程池提供的第2個(gè)功能。

              有的時(shí)候,應(yīng)用程序需要在某一個(gè)特定的時(shí)間執(zhí)行特定的任務(wù),你可以選擇使用Windows內(nèi)核對(duì)象“等待定時(shí)器”來(lái)實(shí)現(xiàn)這個(gè)功能,但是如果這種基于時(shí)間的任務(wù)特別的多,那么就不得不為每個(gè)這樣的任務(wù)創(chuàng)建一個(gè)“等待定時(shí)器”對(duì)象,無(wú)疑會(huì)浪費(fèi)資源。當(dāng)然,你也許會(huì)想到創(chuàng)建單個(gè)“等待定時(shí)器”,然后不斷地設(shè)置它的下一次要等待的時(shí)間,這樣就可以完成多個(gè)基于時(shí)間的任務(wù)了。但是如此一來(lái),代碼量就會(huì)增大。

              Windows提供了線程池來(lái)實(shí)現(xiàn)這樣的功能,其方法是通過(guò)“線程池定時(shí)器”。

             

              首先,你要定義一個(gè)如下格式的回調(diào)函數(shù),讓線程池中的線程定時(shí)調(diào)用它:

            VOID CALLBACK TimeoutCallback(     // 函數(shù)名可以任意
               PTP_CALLBACK_INSTANCE pInstance,
               PVOID pvContext,         
            // 函數(shù)的參數(shù)
               PTP_TIMER pTimer);       // 一個(gè)指向“線程池定時(shí)器”的指針

             

              然后告訴線程池什么時(shí)候調(diào)用你的回調(diào)函數(shù):

            PTP_TIMER CreateThreadpoolTimer(
               PTP_TIMER_CALLBACK pfnTimerCallback,   
            // 類(lèi)似上面格式的函數(shù)的指針
               PVOID pvContext,               // 回調(diào)函數(shù)的參數(shù),由這個(gè)參數(shù)指明
               PTP_CALLBACK_ENVIRON pcbe);

             

              不難發(fā)現(xiàn),CreateThreadpoolTimer函數(shù)和第1種方法中的CreateThreadpoolWork函數(shù)十分類(lèi)似,而且兩者的回調(diào)函數(shù)也十分類(lèi)似。當(dāng)調(diào)用CreateThreadpoolTimer函數(shù)的時(shí)候,第1個(gè)參數(shù)指向一個(gè)回調(diào)函數(shù),第2個(gè)參數(shù)pvContext會(huì)傳遞給這個(gè)回調(diào)函數(shù)的第2個(gè)參數(shù),而其返回值——一個(gè)“線程池定時(shí)器”指針也會(huì)傳遞給這個(gè)回調(diào)函數(shù)的第3個(gè)參數(shù)。

              如果你想把由CreateThreadpoolTimer函數(shù)創(chuàng)建的“線程池定時(shí)器”注冊(cè)到線程池中去,可以使用如下函數(shù):

            VOID SetThreadpoolTimer(
               PTP_TIMER pTimer,        
            // 一個(gè)“線程池定時(shí)器”指針
               PFILETIME pftDueTime,    // 回調(diào)函數(shù)被調(diào)用的時(shí)間
               DWORD msPeriod,          // 周期性調(diào)用回調(diào)函數(shù)的間隔時(shí)間(毫秒)
               DWORD msWindowLength);   // 周期時(shí)間的波動(dòng)范圍(毫秒)

             

              該函數(shù)的第1個(gè)參數(shù)pTimer是由CreateThreadpoolTimer函數(shù)返回的。第2個(gè)參數(shù)pftDueTimer是指明回調(diào)函數(shù)什么時(shí)候被調(diào)用,一個(gè)正的數(shù)值表示的是絕對(duì)時(shí)間,即UTC統(tǒng)一時(shí)間;一個(gè)負(fù)數(shù)表示相對(duì)時(shí)間,即調(diào)用該函數(shù)之后開(kāi)始計(jì)時(shí),以毫秒為單位;如果是-1,表明回調(diào)函數(shù)馬上被調(diào)用。第3個(gè)參數(shù)msPeriod表明周期性地調(diào)用回調(diào)函數(shù)的時(shí)間間隔,即周期時(shí)間,如果只想回調(diào)函數(shù)調(diào)用一次,傳遞0給這個(gè)參數(shù)。第4個(gè)參數(shù)是和第3個(gè)參數(shù)聯(lián)用的,表明周期時(shí)間的波動(dòng)范圍,比如,msPeriod=1000,msWindowLength=2,那么回調(diào)函數(shù)會(huì)在每隔998、999、1000、1001、1002這5個(gè)可能的毫秒時(shí)間被調(diào)用。

              如果一個(gè)“線程池定時(shí)器”已經(jīng)被SetThreadpoolTimer設(shè)置了,那么可以再次呼叫SetThreadpoolTimer函數(shù)來(lái)更改它的相關(guān)屬性。呼叫SetThreadpoolTimer的時(shí)候,可以把NULL傳遞給第2個(gè)參數(shù)pftDueTime,這樣就說(shuō)明讓線程池停止呼叫對(duì)應(yīng)的回調(diào)函數(shù)。

              你可以查詢(xún)一個(gè)“線程池定時(shí)器”是否被設(shè)置,呼叫IsThreadpoolTimerSet函數(shù):

            BOOL IsThreadpoolTimerSet(PTP_TIMER pti);

             

              你也可以讓線程等待一個(gè)“線程池定時(shí)器”完成工作,呼叫函數(shù)WaitForThreadpoolTimerCallbacks,當(dāng)要關(guān)閉一個(gè)“線程池定時(shí)器”的時(shí)候,呼叫函數(shù)CloseThreadpoolTimer,這兩個(gè)函數(shù)同前面討論的WaitForThreadpoolWork和CloseThreadpoolWorkCallbacks函數(shù)類(lèi)似,可以參考本篇前面的內(nèi)容。

             

              下面總結(jié)一下“線程池定時(shí)器”的使用方法:

            1. 定義一個(gè)回調(diào)函數(shù),如TimeoutCallback那樣的格式。
            2. 使用CreateThreadpoolTimer函數(shù)創(chuàng)建一個(gè)“線程池定時(shí)器”,并將已定義的回調(diào)函數(shù)與它關(guān)聯(lián)在了一起。
            3. 使用SetThreadpoolTimer設(shè)置“線程池定時(shí)器”的屬性,并將其提交給線程池。
            4. 調(diào)用CloseThreadpoolTimer關(guān)閉“線程池定時(shí)器”。

             

            當(dāng)一個(gè)內(nèi)核對(duì)象被通知的時(shí)候調(diào)用一個(gè)函數(shù)

              有很多線程,初始化的時(shí)候等待一個(gè)內(nèi)核對(duì)象,一旦這個(gè)內(nèi)核對(duì)象轉(zhuǎn)入“已通知”狀態(tài),線程就會(huì)通知另外一些線程,然后轉(zhuǎn)回繼續(xù)等待這個(gè)內(nèi)核對(duì)象。但是,如果這樣的線程很多的話,無(wú)疑會(huì)增大系統(tǒng)的開(kāi)銷(xiāo)。

              此時(shí),你可以考慮使用線程池來(lái)實(shí)現(xiàn)這個(gè)功能,就是當(dāng)一個(gè)內(nèi)核對(duì)象被通知的時(shí)候,由線程池中的一個(gè)線程調(diào)用一個(gè)異步的回調(diào)函數(shù)。

               如果你想讓一個(gè)“工作項(xiàng)”在一個(gè)內(nèi)核對(duì)象為“已通知”的狀態(tài)下被執(zhí)行,這個(gè)基本流程和前面兩個(gè)功能的流程類(lèi)似。

              首先,定義一個(gè)如下格式的異步函數(shù):

            VOID CALLBACK WaitCallback(     // 函數(shù)名可以任意
               PTP_CALLBACK_INSTANCE pInstance,
               PVOID Context,          
            // 函數(shù)的參數(shù)
               PTP_WAIT Wait,         // 線程池等待對(duì)象的指針
               TP_WAIT_RESULT WaitResult);     // 該函數(shù)被調(diào)用的原因

             

              然后,需要?jiǎng)?chuàng)建一個(gè)“線程池等待對(duì)象”:

            PTP_WAIT CreateThreadpoolWait(
               PTP_WAIT_CALLBACK    pfnWaitCallback,     
            // 回調(diào)函數(shù)指針,函數(shù)如上定義
               PVOID                pvContext,          // 傳遞給回調(diào)函數(shù)參數(shù)Context
               PTP_CALLBACK_ENVIRON pcbe);

             

              接著就可以將創(chuàng)建的“線程池等待對(duì)象”與這個(gè)線程池關(guān)聯(lián)起來(lái),此時(shí)線程池隊(duì)列中會(huì)有一個(gè)“等待項(xiàng)”記錄:

            VOID SetThreadpoolWait(
               PTP_WAIT  pWaitItem,     
            // 一個(gè)“線程池等待對(duì)象”指針
               HANDLE    hObject,       // 一個(gè)內(nèi)核對(duì)象句柄,當(dāng)被通知時(shí),回調(diào)函數(shù)被調(diào)用
               PFILETIME pftTimeout);   // 等待hObjetct內(nèi)核對(duì)象受到通知的時(shí)間

             

              這個(gè)函數(shù)的第1個(gè)參數(shù)pWaitItem很顯然是從CreateThreadpoolWait成功返回的“線程池等待對(duì)象”指針。第2個(gè)參數(shù)hObject是一個(gè)內(nèi)核對(duì)象句柄,當(dāng)這個(gè)內(nèi)核對(duì)象為“已通知”狀態(tài),則線程池中的一個(gè)線程調(diào)用異步回調(diào)函數(shù)。第3個(gè)參數(shù)pftTimeout是一個(gè)等待內(nèi)核對(duì)象的時(shí)間,如果為0表示不等待;傳遞一個(gè)負(fù)數(shù)表示一個(gè)相對(duì)時(shí)間;傳遞一個(gè)正數(shù)表示絕對(duì)時(shí)間;傳遞NULL表示無(wú)限期地等待。

              要注意的是,不要多次使用SetThreadpoolWait來(lái)等待同一個(gè)hObject。

              當(dāng)內(nèi)核對(duì)象被通知或者等待時(shí)間超出,線程池中的線程將呼叫你的回調(diào)函數(shù),這個(gè)回調(diào)函數(shù)的最后一個(gè)參數(shù)WaitResult的值,其實(shí)是一個(gè)DOWRD類(lèi)型的,它指明的該回調(diào)函數(shù)被調(diào)用的原因:

            1、WAIT_OBJECT_0:SetThreadpoolWait中第二個(gè)參數(shù)hObject所表明的內(nèi)核對(duì)象受到通知。

            2、WAIT_TIMEOUT:內(nèi)核對(duì)象受到通知的時(shí)間超過(guò)了SetThreadpoolWait的第三個(gè)參數(shù)所設(shè)置的等待時(shí)間。

            3、WAIT_ABANDONED_0:SetThreadWait函數(shù)第二個(gè)參數(shù)hObject代表一個(gè)互斥內(nèi)核對(duì)象,而這個(gè)互斥內(nèi)核對(duì)象被丟棄。

             

              一旦一個(gè)線程池線程調(diào)用了你的回調(diào)函數(shù),那么對(duì)應(yīng)的“等待項(xiàng)”就不活躍了,你必須使用相同的參數(shù)再次調(diào)用SetThreadpoolWait函數(shù)來(lái)提交一個(gè)等待項(xiàng)。

              如果想刪除一個(gè)“等待項(xiàng)”,可以使用與之對(duì)應(yīng)的“線程池等待對(duì)象”指針來(lái)調(diào)用SetThreadpoolWait,并將hObejct參數(shù)設(shè)置為NULL。

              最后,你也可以使用WaitForThreadpoolWaitCallbacks來(lái)等待對(duì)應(yīng)的“等待項(xiàng)”結(jié)束,也可以使用CloseThreadpoolWait來(lái)關(guān)閉一個(gè)“等待項(xiàng)”。這兩個(gè)參數(shù)和WaitForThreadpoolWorkCallbakcs和CloseThreadpoolWork是類(lèi)似的。

             

            當(dāng)異步I/O請(qǐng)求結(jié)束的時(shí)候調(diào)用一個(gè)函數(shù)

              讀過(guò)上面3中線程池的功能,不難發(fā)現(xiàn)有很多共同的特點(diǎn),連函數(shù)名稱(chēng)都很有規(guī)律。線程池中的線程由系統(tǒng)統(tǒng)一管理,自動(dòng)地創(chuàng)建和銷(xiāo)毀。其實(shí),這些線程內(nèi)部都在等待一個(gè)I/O完成端口,這個(gè)I/O完成端口稱(chēng)為“線程池的I/O完成端口”。

              如果你要使用線程池來(lái)處理設(shè)備異步I/O請(qǐng)求的時(shí)候,當(dāng)你打開(kāi)一個(gè)設(shè)備的時(shí)候,必須首先將這個(gè)設(shè)備與“線程池I/O完成端口”關(guān)聯(lián)起來(lái),然后告訴線程池當(dāng)設(shè)備異步I/O請(qǐng)求結(jié)束之后哪個(gè)函數(shù)將被調(diào)用。

             

              首先,定義一個(gè)如下格式的異步回調(diào)函數(shù):

            VOID CALLBACK OverlappedCompletionRoutine(     // 函數(shù)名可以任意
                PTP_CALLBACK_INSTANCE pInstance,
                PVOID          pvContext,     
            // 該函數(shù)的一個(gè)參數(shù)
                PVOID          pOverlapped,  // OVERLAPPED結(jié)構(gòu)指針
                ULONG         IoResult,        // I/O請(qǐng)求結(jié)果,如果成功,則為NO_ERROR
                ULONG_PTR  NumberOfBytesTransferred,     // I/O請(qǐng)求的數(shù)據(jù)傳輸字節(jié)數(shù)
                PTP_IO         pIo);             // 一個(gè)“線程池I/O完成項(xiàng)”指針

             

              這個(gè)函數(shù)的最后一個(gè)參數(shù)pIo是一個(gè)PTP_IO類(lèi)型,即一個(gè)線程池I/O完成項(xiàng),它與“線程池工作項(xiàng)”和“線程池等待項(xiàng)”是類(lèi)似的。你必須創(chuàng)建它,使用如下函數(shù):

            PTP_IO CreateThreadpoolIo(
                HANDLE       hDevice,     
            // 與線程池I/O完成端口關(guān)聯(lián)的設(shè)備對(duì)象句柄
                PTP_WIN32_IO_CALLBACK pfnIoCallback,     // 如上格式的異步回調(diào)函數(shù)指針
                PVOID        pvContext,   // 該參數(shù)在調(diào)用時(shí)傳遞給回調(diào)函數(shù)的第2個(gè)參數(shù)
                PTP_CALLBACK_ENVIRON  pcbe);

             

              該函數(shù)將hDevice參數(shù)所對(duì)應(yīng)的設(shè)備記錄到線程池I/O項(xiàng)中,然后,可以使用如下函數(shù)將設(shè)備與線程池I/O完成端口關(guān)聯(lián)起來(lái):

            VOID StartThreadpoolIo(PTP_IO pio);

             

              注意,StartThreadpoolIo函數(shù)必須在ReadFile和WriteFile之前調(diào)用,如果沒(méi)有在它們之前調(diào)用,你的異步回調(diào)函數(shù)不會(huì)被調(diào)用。

              當(dāng)你想停止調(diào)用回調(diào)函數(shù)的時(shí)候,可以使用CancelThreadpoolIo,如果在調(diào)用ReadFile或WriteFile之后,它們的返回值是FLASE,而GetLastError的返回值不是ERROR_IO_PENDING,那么也應(yīng)該調(diào)用CancelThreadpoolIo:

            VOID CancelThreadpoolIo(PTP_IO pio);

             

              當(dāng)結(jié)束了設(shè)備I/O,你應(yīng)該使用CloseHandle關(guān)閉設(shè)備句柄,然后呼叫CloseThradpoolIo關(guān)閉線程池I/O項(xiàng),即取消設(shè)備與線程池I/O請(qǐng)求的關(guān)聯(lián)。

            VOID CloseThreadpoolIo(PTP_IO pio);

             

              另外,你可以讓一個(gè)線程等待I/O請(qǐng)求結(jié)束:

            VOID WaitForThreadpoolIoCallbacks(
               PTP_IO pio,
               BOOL bCancelPendingCallbacks);     
            // 是否取消回調(diào)函數(shù)的調(diào)用

             

              如果給這個(gè)函數(shù)的參數(shù)BCancelPendingCallbacks傳遞TRUE,那么回調(diào)函數(shù)將不會(huì)被調(diào)用,該函數(shù)的用法和WaitForThreadpoolWork是類(lèi)似的。

             

            回調(diào)函數(shù)結(jié)束之后的行為

              注意上面討論的各種類(lèi)型的回調(diào)函數(shù)第1個(gè)參數(shù),是一個(gè)PTF_CALLBACK_INSTANCE類(lèi)型的數(shù)據(jù)pInstance,從字面上看,是“線程池回調(diào)函數(shù)實(shí)體指針”,也就是說(shuō),這個(gè)數(shù)據(jù)是各個(gè)回調(diào)函數(shù)唯一的,是回調(diào)函數(shù)的標(biāo)識(shí),這個(gè)數(shù)據(jù)是在調(diào)用回調(diào)函數(shù)之前由系統(tǒng)自動(dòng)分配的,可以用這個(gè)參數(shù)調(diào)用如下函數(shù):

             

            // 當(dāng)回調(diào)函數(shù)返回的時(shí)候,自動(dòng)離開(kāi)一個(gè)指定的關(guān)鍵代碼段
            VOID LeaveCriticalSectionWhenCallbackReturns(
                 PTP_CALLBACK_INSTANCE pci,     
            // 回調(diào)函數(shù)實(shí)體指針,標(biāo)識(shí)一個(gè)回調(diào)函數(shù)
                 PCRITICAL_SECTION pcs);      // 關(guān)鍵代碼段結(jié)構(gòu)指針,標(biāo)識(shí)一個(gè)關(guān)鍵代碼段

            // 當(dāng)回調(diào)函數(shù)返回的時(shí)候,自動(dòng)釋放一個(gè)指定的互斥內(nèi)核對(duì)象
            VOID ReleaseMutexWhenCallbackReturns(
                 PTP_CALLBACK_INSTANCE pci,
                 HANDLE mut);

            // 當(dāng)回調(diào)函數(shù)返回的時(shí)候,自動(dòng)釋放一個(gè)指定的信號(hào)量?jī)?nèi)核對(duì)象
            VOID ReleaseSemaphoreWhenCallbackReturns(
                 PTP_CALLBACK_INSTANCE pci,
                 HANDLE sem,
                 DWORD crel);

            // 當(dāng)回調(diào)函數(shù)返回的時(shí)候,自動(dòng)將一個(gè)事件內(nèi)核對(duì)象設(shè)置為已通知狀態(tài)
            VOID SetEventWhenCallbackReturns(
                 PTP_CALLBACK_INSTANCE pci,
                 HANDLE evt);

            // 當(dāng)回調(diào)函數(shù)返回的時(shí)候,自動(dòng)卸載一個(gè)模塊
            VOID FreeLibraryWhenCallbackReturns(
                 PTP_CALLBACK_INSTANCE pci,
                 HMODULE mod);

             

              這些函數(shù)的第1個(gè)參數(shù)pci標(biāo)識(shí)當(dāng)線程池前正在處理的工作、定時(shí)器、等待、I/O項(xiàng),調(diào)用這些函數(shù),表示對(duì)應(yīng)的回調(diào)函數(shù)結(jié)束之后,所做的一些釋放和設(shè)置工作。

              其中,前4個(gè)函數(shù),提供了一種方法來(lái)通知其他線程,說(shuō)明線程池中的某一個(gè)工作項(xiàng)完成。最后一個(gè)函數(shù),提供了一種方法來(lái)卸載DLL的方法,特別是當(dāng)回調(diào)函數(shù)是從DLL中導(dǎo)出的時(shí)候,這種方法特別適用。要注意的是,只能有一個(gè)動(dòng)作在回調(diào)函數(shù)返回的時(shí)候被執(zhí)行,你不能多次呼叫上述5個(gè)函數(shù),這樣的話,最后一次呼叫的函數(shù)會(huì)覆蓋前面所呼叫的函數(shù),因此,不能同時(shí)離開(kāi)關(guān)鍵代碼段并釋放信號(hào)量?jī)?nèi)核對(duì)象。

              另外,還有兩個(gè)函數(shù)需要回調(diào)函數(shù)實(shí)體指針:

            BOOL CallbackMayRunLong(PTP_CALLBACK_INSTANCE pci);
            VOID DisassociateCurrentThreadFromCallback(PTP_CALLBACK_INSTANCE pci);

             

              CallbackMayRunLong并不是設(shè)置回調(diào)函數(shù)結(jié)束時(shí)的工作的,而是當(dāng)一個(gè)回調(diào)函數(shù)認(rèn)為自己執(zhí)行的時(shí)間可能比較長(zhǎng)才可能需要呼叫這個(gè)函數(shù)。此時(shí),線程池不會(huì)創(chuàng)建新的線程,以此來(lái)提高這個(gè)回調(diào)函數(shù)的性能。當(dāng)該函數(shù)返回FLASE,線程池不允許其他線程能夠處理線程池隊(duì)列中的其他項(xiàng);如果返回TRUE,表示線程池允許其他線程處理其他工作項(xiàng)。

              DisassociateCurrentThreadFromCallback函數(shù)表明與回調(diào)函數(shù)關(guān)聯(lián)的工作項(xiàng)在“邏輯上”完成了(其實(shí)不是真正完成),此時(shí)允許等待在這個(gè)工作項(xiàng)上的函數(shù)返回,比如WaitForThreadpoolWorkCallbacks、WaitForThreadpoolTimerCallbacks、WaitForThreadpoolWaitCallbacks、WaitForThreadpoolIoCallbacks這些函數(shù)返回。

             

            定制線程池

              以上所討論的線程池,都是系統(tǒng)自動(dòng)控制的,用戶無(wú)法改變其內(nèi)部的流程。

              下面,我們討論一下如何自己定制線程池。

              你會(huì)注意到,上面的每個(gè)“創(chuàng)建”函數(shù):CreateThreadpoolWork、CreateThreadpoolTimer、CreateThreadpoolWait、CreateThreadpoolIo以及TrySubmitThreadpoolCallback這5個(gè)函數(shù)中的最后一個(gè)參數(shù)pcbe,一個(gè)類(lèi)型為PTP_CALLBACK_ENVIRON的參數(shù),一個(gè)指向“回調(diào)函數(shù)環(huán)境”結(jié)構(gòu)的指針。你可以簡(jiǎn)單地傳遞NULL給這個(gè)參數(shù),表明你使用默認(rèn)的系統(tǒng)自動(dòng)分配和管理的進(jìn)程線程池。

              但是,有的時(shí)候程序員喜歡自己來(lái)控制線城池,給線城池設(shè)置一些規(guī)則和屬性。比如設(shè)置改線城池中線程的數(shù)量上下限,或者想操縱線城池中線程的創(chuàng)建和銷(xiāo)毀。

              要達(dá)到這個(gè)目的,可以自己創(chuàng)建線程池,然后設(shè)置一些屬性。

              首先,創(chuàng)建一個(gè)線程池,使CreateThreadpool函數(shù):

            PTP_POOL CreateThreadpool(PVOID reserved);  // 參數(shù)是保留參數(shù),必須為NULL

             

              該函數(shù)返回一個(gè)PTP_POOL類(lèi)型的數(shù)據(jù),姑且認(rèn)為是“線城池指針”的意思,即代表了一個(gè)線程池。

              然后,就可以通過(guò)這個(gè)線城池指針來(lái)呼叫相應(yīng)的API函數(shù),設(shè)置線程池的一些屬性了。

              你可以設(shè)置線城池的線程數(shù)量的上下限:

            BOOL SetThreadpoolThreadMinimum(

                 PTP_POOL pThreadPool,

                 DWORD cthrdMin);


            BOOL SetThreadpoolThreadMaximum(

                 PTP_POOL pThreadPool,

                 DWORD cthrdMost);

             

              通過(guò)呼叫這兩個(gè)函數(shù)之后,線程池中的線程的數(shù)量決不會(huì)少于設(shè)置的最小值,并且允許這個(gè)數(shù)量增大到最大值。順便說(shuō)一下,默認(rèn)的線城池的線程數(shù)量范圍為1~500。

              當(dāng)一個(gè)線程池需要關(guān)閉的時(shí)候,呼叫CloseThreadpool函數(shù):

            VOID CloseThreadpool(PTP_POOL pThreadPool);

             

              呼叫這個(gè)函數(shù)之后,對(duì)應(yīng)的線程池隊(duì)列中的項(xiàng)都不會(huì)被處理,當(dāng)前正在處理工作項(xiàng)的線程都會(huì)結(jié)束處理然后線程終止,其他還沒(méi)有被處理的項(xiàng)都會(huì)被取消。

              一旦你創(chuàng)建了你自己的線程池并設(shè)置了線程數(shù)量上下限,你就初始化那個(gè)“回調(diào)函數(shù)環(huán)境”結(jié)構(gòu)了,該結(jié)構(gòu)中包含了另外的一些設(shè)置。這個(gè)結(jié)構(gòu)與一個(gè)工作項(xiàng)有關(guān)。該結(jié)構(gòu)定義如下:

            typedef struct _TP_CALLBACK_ENVIRON {
               TP_VERSION  Version;
               PTP_POOL      Pool;     
            // 所屬哪個(gè)線程池
               PTP_CLEANUP_GROUP  CleanupGroup;
               PTP_CLEANUP_GROUP_CANCEL_CALLBACK CleanupGroupCancelCallback;
               PVOID           RaceDll;
               
            struct _ACTIVATION_CONTEXT  *ActivationContext;
               PTP_SIMPLE_CALLBACK             FinalizationCallback;

               union {
                  DWORD  Flags;
                  
            struct {
                     DWORD  LongFunction :  
            1;
                     DWORD  Private      : 
            31;
                  } s;
               } u;
            } TP_CALLBACK_ENVIRON, 
            *PTP_CALLBACK_ENVIRON;

             

              你最好不要自己去初始化該結(jié)構(gòu),而是應(yīng)該使用如下函數(shù):

            VOID InitializeThreadpoolEnvironment(PTP_CALLBACK_ENVIRON pcbe);

             

              該函數(shù)將結(jié)構(gòu)中的各個(gè)字段設(shè)置為0,除了Version被設(shè)置為1。

              當(dāng)你不再需要使用該結(jié)構(gòu)的時(shí)候,使用如下函數(shù)刪除它:

            VOID DestroyThreadpoolEnvironment(PTP_CALLBACK_ENVIRON pcbe);

             

               在初始化TP_CALLBACK_ENVIRON結(jié)構(gòu)之后,該結(jié)構(gòu)與一個(gè)工作項(xiàng)相關(guān),然后你就可以使用這個(gè)結(jié)構(gòu)來(lái)提交一個(gè)工作項(xiàng)給指定的線程池了:

            VOID SetThreadpoolCallbackPool(
                 PTP_CALLBACK_ENVIRON pcbe, 
            // 回調(diào)函數(shù)環(huán)境結(jié)構(gòu)指針
                 PTP_POOL pThreadPool);     // 線程池指針,由CreateThreadpool函數(shù)返回

             

              如果不調(diào)用該函數(shù),那么回調(diào)函數(shù)環(huán)境結(jié)構(gòu)中的Pool成員的值就是NULL,表示不為任何定制的線程池相關(guān),那么與此結(jié)構(gòu)相關(guān)的工作項(xiàng)就會(huì)提交給默認(rèn)線程池。

              如果一個(gè)工作項(xiàng)處理的時(shí)間比較長(zhǎng),可以調(diào)用SetThreadpoolCallbackRunsLong函數(shù),這會(huì)導(dǎo)致線程池更快地創(chuàng)建線程來(lái)處理這個(gè)工作項(xiàng)。

            VOID SetThreadpoolCallbackRunsLong(PTP_CALLBACK_ENVIRON pcbe);

             

              你可以呼叫SetThreadpoolCallbackLibrary來(lái)確保當(dāng)某個(gè)工作項(xiàng)未完成的時(shí)候,一個(gè)DLL始終被加載到進(jìn)程地址中。該函數(shù)也可以除去潛在的死鎖。

            VOID SetThreadpoolCallbackLibrary(
                 PTP_CALLBACK_ENVIRON pcbe,
                 PVOID mod);     
            // 模塊指針,就是模塊句柄

             

              線程池要處理很多的項(xiàng)目,因此很難確定這些項(xiàng)目什么時(shí)候處理結(jié)束,這也使得線程池的清除工作困難了。為了解決這種情況,線程池提供了一種機(jī)制——“清理組”。要注意的是,該機(jī)制不適合于默認(rèn)線程池,只適合于定制的線程池。因?yàn)槟J(rèn)線程池的生命期與進(jìn)程一樣,系統(tǒng)會(huì)在進(jìn)程結(jié)束之后清除默認(rèn)線程池。

              為了使用“清理組”機(jī)制,首先,你需要?jiǎng)?chuàng)建一個(gè)清理組:

            PTP_CLEANUP_GROUP CreateThreadpoolCleanupGroup();

             

              然后將已創(chuàng)建的清理組與線程池關(guān)聯(lián)起來(lái):

            VOID SetThreadpoolCallbackCleanupGroup(
              PTP_CALLBACK_ENVIRON pcbe,     
            // 回調(diào)函數(shù)環(huán)境結(jié)構(gòu)指針
              PTP_CLEANUP_GROUP ptpcg,        // 清理組指針
              PTP_CLEANUP_GROUP_CANCEL_CALLBACK pfng);     // 回調(diào)函數(shù)指針


              該函數(shù)在內(nèi)部將回調(diào)函數(shù)指針p的cbe參數(shù)CleanupGroup和CleanupCancelCallback成員設(shè)置為第2和第3個(gè)參數(shù)所提供的值。如果清理組被取消,第3個(gè)參數(shù)pfng表示的回調(diào)函數(shù)將會(huì)被調(diào)用,這個(gè)回調(diào)函數(shù)必須滿足如下格式:

            VOID CALLBACK CleanupGroupCancelCallback(     // 函數(shù)名可以任意
              PVOID pvObjectContext,
              PVOID pvCleanupContext);


              每次你調(diào)用CreateThreadpoolWork、CreateThreadpoolTimer、CreateThreadpoolWait和CreateThreadpoolIo的時(shí)候,如果傳遞給它們的最后一個(gè)參數(shù)pcbe不是NULL,那么一項(xiàng)就會(huì)加入相關(guān)的組清理中,當(dāng)這樣的項(xiàng)都完成之后,你調(diào)用CloseThreadpoolWork、CloseThreadpoolTimer、CloseThreadpoolWait、ClsoeThreadpoolIo的時(shí)候,又暗中地將這樣的項(xiàng)從組清理中刪除了。

              這個(gè)時(shí)候,想要?jiǎng)h除線程池,可以調(diào)用如下函數(shù):

            VOID CloseThreadpoolCleanupGroupMembers(
              PTP_CLEANUP_GROUP ptpcg,      
            // 組清理指針
              BOOL bCancelPendingCallbacks, // 是否取消即將開(kāi)始執(zhí)行的回調(diào)函數(shù)
              PVOID pvCleanupContext);      // 傳入CleanupGroupCancelCallback的參數(shù)

             

              這個(gè)函數(shù)同以前的WaitForThreadpool*函數(shù)類(lèi)似,當(dāng)一個(gè)線程呼叫它時(shí),它等待,直到所有的在線程工作組的項(xiàng)完成處理。如果第2個(gè)參數(shù)為T(mén)RUE,會(huì)取消所有還沒(méi)有開(kāi)始執(zhí)行的項(xiàng)目,然后等待當(dāng)前正在執(zhí)行的項(xiàng)目,直到它們執(zhí)行完成后該函數(shù)返回。如果第2個(gè)參數(shù)為T(mén)RUE,而且SetThreadpoolCallbackCleanupGroup的最后一個(gè)參數(shù)pfng傳遞了一個(gè)回調(diào)函數(shù)指針,那么這個(gè)回調(diào)函數(shù)就會(huì)為每個(gè)被取消的項(xiàng)目調(diào)用一次,CloseThreadpoolCleanupGroupMembers函數(shù)的第3個(gè)參數(shù)pvCleanupContext就會(huì)傳入回調(diào)函數(shù)的第2個(gè)參數(shù)pvCleanupContext。

             

              如果在呼叫CloseThreadpoolCleanupGroupMembers函數(shù)的時(shí)候,傳遞FLASE給第2個(gè)參數(shù),那么該函數(shù)就會(huì)等待線程池中隊(duì)列中的所有的項(xiàng)完成之后才返回,此時(shí)回調(diào)函數(shù)不會(huì)被調(diào)用,因此可以傳遞NULL給pvCleanupContext參數(shù)。

             

              當(dāng)ClsetThreadpoolCleanupGroupMembers函數(shù)返回之后,你需要呼叫CloseThreadpoolCleanupGroup來(lái)關(guān)閉一個(gè)線程池清理組:

            VOID WINAPI CloseThreadpoolCleanupGroup(PTP_CLEANUP_GROUP ptpcg);

             

              最后,要調(diào)用DestroyThreadpoolEvironment和CloseThreadpool函數(shù)來(lái)清除回調(diào)函數(shù)環(huán)境結(jié)構(gòu)和關(guān)閉線程池。

             

            小結(jié)

              通過(guò)前面的敘述,線程池的操作流程是比較固定的:

            1、定義異步回調(diào)函數(shù)

            2、創(chuàng)建相關(guān)項(xiàng)

            3、提交或設(shè)置項(xiàng)

            4、線程池執(zhí)行

            5、關(guān)閉相關(guān)項(xiàng)

             

              自己寫(xiě)了一段代碼,總結(jié)了前面的知識(shí),代碼中省略了第1步,即沒(méi)有定義回調(diào)函數(shù),因?yàn)檫@是根據(jù)需要而編寫(xiě)的。如果代碼中有錯(cuò)誤,還請(qǐng)大家指出。

            PTP_POOL pThreadpool = CreateThreadpool(NULL);    // 創(chuàng)建線程池

            // 設(shè)置線程池線程數(shù)量上下限
            SetThreadpoolThreadMinimum(pThreadpool, 2);
            SetThreadpoolThreadMaximum(pThreadpool, 
            10);

            // 初始化“回調(diào)函數(shù)環(huán)境”結(jié)構(gòu)
            TP_CALLBACK_ENVIRON tcbe;
            InitializeThreadpoolEnvironment(
            &tcbe);

            // 將該回調(diào)函數(shù)環(huán)境結(jié)構(gòu)與線程池相關(guān)聯(lián)
            SetThreadpoolCallbackPool(&tcbe, pThreadpool);

            // 創(chuàng)建清理組
            PTP_CLEANUP_GROUP pTpcg= CreateThreadpoolCleanupGroup();

            // 將回調(diào)函數(shù)環(huán)境結(jié)構(gòu)與清理組關(guān)聯(lián)起來(lái)
            SetThreadpoolCallbackCleanupGroup(&tcbe, pTpcg, NULL);

            // 現(xiàn)在可以創(chuàng)建一些項(xiàng),提交給線程池
            PTP_WORK pTpWork 
            = CreateThreadpoolWork(&tcbe);// 創(chuàng)建一個(gè)工作項(xiàng)
            SubmitThreadpoolWork(pTpWork);    // 提交工作項(xiàng)
            PTP_TIMER pTpTimer = CreateThreadpoolTimer(&tcbe);// 創(chuàng)建一個(gè)定時(shí)器項(xiàng)
            SetThreadpoolTimer(pTpTimer, );      // 提交定時(shí)器
            PTP_WAIT pTpWait = CreateThreadpoolWait(&tcbe);// 創(chuàng)建一個(gè)等待項(xiàng)
            SetThreadpoolWait(pTpWait, );    // 提交等待項(xiàng)
            PTP_IO pTpIO = CreateThreadpoolIo(&tcbe);    // 創(chuàng)建一個(gè)IO項(xiàng)
            StartThreadpoolIo(pTpIO);    // 開(kāi)始執(zhí)行IO項(xiàng)

            // 等待所有項(xiàng)完成
            CloseThreadpoolCleanupGroupMembers(pTpcg, FALSE, NULL);

            // 關(guān)閉各個(gè)項(xiàng)
            CloseThreadpoolWork(pTpWork);
            CloseThreadpoolTimer(pTpTimer);
            CloseThreadpoolWait(pTpWait);
            CloseThreadpoolIo(pTpIO);

            CloseThreadpoolCleanupGroup(pTpcg);    
            // 關(guān)閉線程池清理組
            DestroyThreadpoolEnvironment(&tcbe);    // 刪除回調(diào)函數(shù)環(huán)境結(jié)構(gòu)
            CloseThreadpool(pThreadpool);    // 關(guān)閉線程池
            posted on 2011-10-10 09:25 Yu_ 閱讀(845) 評(píng)論(0)  編輯 收藏 引用 所屬分類(lèi): Windows程序設(shè)計(jì)
            久久久青草久久久青草| 青春久久| 久久精品无码专区免费东京热| 婷婷久久综合九色综合绿巨人 | 久久夜色撩人精品国产| 99久久婷婷免费国产综合精品| 久久久精品国产免大香伊 | 亚洲午夜无码久久久久| 久久精品综合网| 久久国内免费视频| 18禁黄久久久AAA片| 久久成人小视频| 亚洲色婷婷综合久久| 国内高清久久久久久| 日产精品99久久久久久| 久久精品国产亚洲AV麻豆网站| 久久综合九色综合网站| 99久久精品国产麻豆| 精品久久久久久综合日本| 狠狠色噜噜狠狠狠狠狠色综合久久 | 国产精品久久一区二区三区| 久久精品国产免费一区| 久久99久久成人免费播放| 日本免费一区二区久久人人澡| 66精品综合久久久久久久| 久久精品国产亚洲Aⅴ蜜臀色欲| 久久影院久久香蕉国产线看观看| 亚洲伊人久久综合中文成人网| 久久强奷乱码老熟女网站| 久久久无码精品亚洲日韩蜜臀浪潮 | 亚洲AV无码久久精品色欲| 久久狠狠高潮亚洲精品| 精品久久久久国产免费| 久久91精品国产91久| 成人免费网站久久久| 久久久国产一区二区三区| 久久www免费人成看片| 精品久久香蕉国产线看观看亚洲| 久久久久香蕉视频| 久久久久亚洲AV无码网站| 精品视频久久久久|