• <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)用程序是非常困難的。需要會面臨兩個大問題。
            一個是要對線程的創(chuàng)建和撤消進(jìn)行管理,另一個是要對線程對資源的訪問實施同步 。 
            線程池(thread pool),允許有多個線程同時存在,并發(fā)執(zhí)行,并且這些線程受到統(tǒng)一管理。

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

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

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

              線程池機制有4種功能:

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

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

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

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

             

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

             

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

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

              第一種:

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

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

             

              第二種:

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

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

            3、將這個工作項提交給線程池

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

             

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

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

             

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

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

             

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

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

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

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

             

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

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

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

             

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

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

             

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

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

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

             

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

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

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

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

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

            VOID CloseThreadpoolWork(PTP_WORK pwk);

             

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

              這是Windows線程池提供的第2個功能。

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

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

             

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

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

             

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

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

             

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

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

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

             

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

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

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

            BOOL IsThreadpoolTimerSet(PTP_TIMER pti);

             

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

             

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

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

             

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

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

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

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

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

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

             

              然后,需要創(chuàng)建一個“線程池等待對象”:

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

             

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

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

             

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

              要注意的是,不要多次使用SetThreadpoolWait來等待同一個hObject。

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

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

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

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

             

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

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

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

             

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

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

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

             

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

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

             

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

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

             

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

            VOID StartThreadpoolIo(PTP_IO pio);

             

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

              當(dāng)你想停止調(diào)用回調(diào)函數(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項,即取消設(shè)備與線程池I/O請求的關(guān)聯(lián)。

            VOID CloseThreadpoolIo(PTP_IO pio);

             

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

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

             

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

             

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

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

             

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

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

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

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

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

             

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

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

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

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

             

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

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

             

            定制線程池

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

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

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

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

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

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

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

             

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

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

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

            BOOL SetThreadpoolThreadMinimum(

                 PTP_POOL pThreadPool,

                 DWORD cthrdMin);


            BOOL SetThreadpoolThreadMaximum(

                 PTP_POOL pThreadPool,

                 DWORD cthrdMost);

             

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

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

            VOID CloseThreadpool(PTP_POOL pThreadPool);

             

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

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

            typedef struct _TP_CALLBACK_ENVIRON {
               TP_VERSION  Version;
               PTP_POOL      Pool;     
            // 所屬哪個線程池
               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)中的各個字段設(shè)置為0,除了Version被設(shè)置為1。

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

            VOID DestroyThreadpoolEnvironment(PTP_CALLBACK_ENVIRON pcbe);

             

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

            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)的工作項就會提交給默認(rèn)線程池。

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

            VOID SetThreadpoolCallbackRunsLong(PTP_CALLBACK_ENVIRON pcbe);

             

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

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

             

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

              為了使用“清理組”機制,首先,你需要創(chuàng)建一個清理組:

            PTP_CLEANUP_GROUP CreateThreadpoolCleanupGroup();

             

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

            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個參數(shù)所提供的值。如果清理組被取消,第3個參數(shù)pfng表示的回調(diào)函數(shù)將會被調(diào)用,這個回調(diào)函數(shù)必須滿足如下格式:

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


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

              這個時候,想要刪除線程池,可以調(diào)用如下函數(shù):

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

             

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

             

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

             

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

            VOID WINAPI CloseThreadpoolCleanupGroup(PTP_CLEANUP_GROUP ptpcg);

             

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

             

            小結(jié)

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

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

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

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

            4、線程池執(zhí)行

            5、關(guān)閉相關(guān)項

             

              自己寫了一段代碼,總結(jié)了前面的知識,代碼中省略了第1步,即沒有定義回調(diào)函數(shù),因為這是根據(jù)需要而編寫的。如果代碼中有錯誤,還請大家指出。

            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)起來
            SetThreadpoolCallbackCleanupGroup(&tcbe, pTpcg, NULL);

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

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

            // 關(guān)閉各個項
            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) 評論(0)  編輯 收藏 引用 所屬分類: Windows程序設(shè)計
            色综合久久久久无码专区| 国产99精品久久| 欧美牲交A欧牲交aⅴ久久| 久久av无码专区亚洲av桃花岛| 国产精品无码久久综合| 久久黄视频| 97超级碰碰碰久久久久| 2021国内精品久久久久久影院| 777米奇久久最新地址| 一级a性色生活片久久无| 伊人久久免费视频| 日韩精品久久久久久久电影蜜臀| 99久久精品这里只有精品| 人妻精品久久无码专区精东影业| 狠狠综合久久综合中文88| 久久精品aⅴ无码中文字字幕重口 久久精品a亚洲国产v高清不卡 | 国产精品欧美久久久久天天影视| 香蕉久久夜色精品国产2020| 国产精品久久国产精麻豆99网站| 狠狠色丁香久久婷婷综合_中 | 97久久超碰国产精品2021| 青青热久久国产久精品| 91亚洲国产成人久久精品| 精品国产乱码久久久久久1区2区| 伊人久久久AV老熟妇色| 少妇被又大又粗又爽毛片久久黑人| 国产精品久久久天天影视| 欧美黑人又粗又大久久久| 久久AV高潮AV无码AV| 国产精品久久久久久久久久影院| 久久天天躁狠狠躁夜夜av浪潮| 99热热久久这里只有精品68| a级成人毛片久久| 久久不射电影网| 99久久99久久精品国产片| 曰曰摸天天摸人人看久久久| 99re这里只有精品热久久| 久久成人影院精品777| 久久99国产精品二区不卡| 色综合合久久天天综合绕视看| 99久久精品毛片免费播放|