Windows核心編程學習筆記(22)--Windows線程池

Drecik學習經驗分享

轉載請註明出處:http://blog.csdn.net/drecik__/article/details/8194020


Windows線程提供了一個線程池機制來簡化線程的創建,銷燬以及日常管理,避免了線程頻繁創建和銷燬的開銷。

一下介紹的與現場池有關的函數是新的線程池API,只能運行在Windwos Vista及以後版本。

這些線程池函數可以幫助我們做以下事情:

  • 以異步的方式來調用一個函數
  • 每隔一段時間調用一個函數
  • 當內核對象出發的時候調用一個函數
  • 當異步I/O請求完成的時候調用一個函數
我們只討論使用默認線程池。

1. 以異步的方式來調用一個函數

有兩種方式實現該過程,下面分別講述:

  • 第一種方法,首先定義一個具有以下原型的函數:
    typedef VOID (NTAPI *PTP_SIMPLE_CALLBACK)(
    	PTP_CALLBACK_INSTANCE Instance,	// 用戶回調函數終止操作,我也沒看懂,傳NULL吧,一般操作都夠用了;
    	PVOID                 Context	// 在加入線程池的時候由用戶傳入的參數;
    	);
    爲了讓自己定義好的函數加入到線程池中,讓線程執行我們的函數,我們需要向線程池提交請求:
    BOOL
    TrySubmitThreadpoolCallback(
    	PTP_SIMPLE_CALLBACK  pfns,	// 自己定義的函數;
    	PVOID                pv,	// 傳給自己定義函數的Context參數;
    	PTP_CALLBACK_ENVIRON pcbe	// 用來自己創建線程池,使用默認線程池傳入NULL;
    	);
    加入成功後,線程池就會有線程來執行我們的函數。
  • 第二種方法:由於每一次調用TrySubmitThreadpoolCallback系統內部的都會以我們的名義分配一個工作項,如果打算提交大量的工作項,那麼可以處於性能和內存使用的考慮,創建工作性一次,然後分多次提交它會更好。
我們使用CreateThreadpoolWork創建一個工作項:
PTP_WORK
CreateThreadpoolWork(
	PTP_WORK_CALLBACK    pfnwk,	// 工作項調用的函數;
	PVOID                pv,	// 傳給調用函數的參數;
	PTP_CALLBACK_ENVIRON pcbe
	);

// PTP_WORK_CALLBACK函數原型;
typedef VOID (NTAPI *PTP_WORK_CALLBACK)(
	PTP_CALLBACK_INSTANCE Instance,
	PVOID                 Context,	// 上面函數傳進來的pv參數;
	PTP_WORK              Work		// 關聯的工作項;
	);

// 向線程池提交一個請求的時候調用下面函數;
// 提交成功後,線程池會有線程來執行我們創建的函數;
VOID
SubmitThreadpoolWork(
	PTP_WORK pwk		// 工作項;
	);

多次提交唯一的缺點就是每一次傳給自定義函數的Context值都相同,如果需要不同的值只能使用TrySubmitThreadpoolCallback函數。

如果我們有另外一個線程,該線程負責取消已經提交的工作項,或者等待工作項處理完畢而需要將自己掛起,則需要調用下面函數:

// 如果工作項未提交,函數就立即返回不執行任何操作;
// 第二個參數如果爲TRUE,那麼該函數會試圖取消先前提交的那個工作項;
// 如果線程池中的線程正在處理那個工作項,該函數會一直等待到工作項完成後才返回;
// 如果已經提交的工作項尚未被任何線程處理,那麼會將它標記會取消,並立即返回,這樣就不會被執行;
// 如果爲FALSE,那麼將會掛起,知道指定工作項的處理已經完成,而且線程池處理該工作項的線程都已經被收回位置;
// 如果用一個PTP_WORK對象提交了多個工作項,傳給第二個參數爲FALSE,就會等待所有工作項完成;
// 如果爲TRUE,只會等待當前正在運行的工作項;
VOID
WaitForThreadpoolWorkCallbacks(
	PTP_WORK pwk,	// 待取消或等待的工作項;
	BOOL     fCancelPendingCallbacks
	);

// 最後一個函數關閉創建的工作項;
VOID
CloseThreadpoolWork(
	PTP_WORK pwk
	);

2. 每個一段時間調用一個函數

① 首先我們必須定義一個如下原型的回調函數:
typedef VOID (NTAPI *PTP_TIMER_CALLBACK)(
	PTP_CALLBACK_INSTANCE Instance,
	PVOID                 Context,	// 傳入的參數;
	PTP_TIMER             Timer		// 關聯的TIME指針;
	);
② 創建一個PTP_TIMER對象:
PTP_TIMER
CreateThreadpoolTimer(
	PTP_TIMER_CALLBACK   pfnti,	// 自己定義的函數指針;
	PVOID                pv,	// 傳入自己定義的函數的參數;
	PTP_CALLBACK_ENVIRON pcbe
	);
③ 註冊計時器:
// 其中開始時間傳入負值表示從現在開始多長時間第一次觸發;
// 如果爲正值,表示一個絕對時間,從1600年1月1日開始計算;
// 單位都爲100納秒;
VOID
SetThreadpoolTimer(
	PTP_TIMER pti,				// 創建的計時器對象;
	PFILETIME pftDueTime,		// 開始時間;
	DWORD     msPeriod,			// 每隔多少時間觸發一次,只觸發一次傳入0;
	DWORD     msWindowLength	// 該參數可以指定在msPeriod+msWindowLength這一時間段內觸發,防止衝突;
	);
設置了計時器之後,還可以調用SetThreadpoolTimer來對計時器進行修改,也可以在pftDueTimer傳入NULL表示停止調用回調函數。

通過IsThreadpoolTimerSet來判斷某個計時器是否被設置。

最後兩個函數WaitForThreadpoolTimerCallbacks和CloseThradpoolTimer與之前討論過的類似,不在討論。


3. 在內核對象出發時調用一個函數

這些線程池操作基本上都大同小異,所以有些參數不在解釋:

// 回調函數;
// 第四個參數表示事件觸發的類型,WAIT_OBJECT_0:關聯的事件觸發;
// WAIT_TIMEOUT:等待超時,WAIT_ABANDONED_0:互斥量遺棄(參考以前的博文);
VOID (NTAPI *PTP_WAIT_CALLBACK)(
	PTP_CALLBACK_INSTANCE Instance,
	PVOID                 Context,
	PTP_WAIT              Wait,
	TP_WAIT_RESULT        WaitResult
	);

PTP_WAIT
CreateThreadpoolWait(
	PTP_WAIT_CALLBACK    pfnwa,
	PVOID                pv,
	PTP_CALLBACK_ENVIRON pcbe
	);

// 關聯內核對象;
// 可多次調用關聯多個事件,但其中一個觸發就會調用回調函數;
// 第三個參數表示等待時間,0表示不等待,NULL表示無限等待,正數表示絕對時間,負數相對時間;
VOID
SetThreadpoolWait(
	PTP_WAIT  pwa,
	HANDLE    h,			// 內核對象;
	PFILETIME pftTimeout
	);

一旦調用回調函數,對應的等待項將不活躍,必須重新關聯該內核對象。

可以在SetThreadpoolWait事件傳入NULL表示將該等待項從內存池中移除。

最後WaitForThreadpoolWaitCallbacks等待等待項完成,CloseThreadpoolWait釋放等待項內存。

最好不要使用PulseEvent觸發事件,因爲無法保證線程池正好在等待該事件。

4. 在異步I/O請求完成時調用一個函數

typedef VOID (WINAPI *PTP_WIN32_IO_CALLBACK)(
	PTP_CALLBACK_INSTANCE Instance,
	PVOID                 Context,
	PVOID                 Overlapped,		// 異步IO傳入的OVERLAPPED結構指針;
	ULONG                 IoResult,			// 是否有錯誤,NO_ERROR表示沒有錯誤;
	ULONG_PTR             NumberOfBytesTransferred,	// 傳輸的字節;
	PTP_IO                Io
	);

PTP_IO
CreateThreadpoolIo(
	HANDLE                fl,		// 設備句柄;
	PTP_WIN32_IO_CALLBACK pfnio,	// 回調函數;
	PVOID                 pv,
	PTP_CALLBACK_ENVIRON  pcbe
	);

// 每次發送異步I/O請求必須與線程池關聯;
VOID
StartThreadpoolIo(
	PTP_IO pio
	);

// 在發出請求之後讓線程停止調用回調函數;
// 如果發送請求的時候調用失敗,則仍然必須調用下面函數;
VOID
	CancelThreadpoolIo(
	PTP_IO pio
	);

// 在關閉設備對象的時候,解除與內存池的關聯;
VOID
	CloseThreadpoolIo(
	PTP_IO pio
	);
同樣還有WaitForThreadpoolIoCallbacks來等待請求的完成。

發佈了61 篇原創文章 · 獲贊 7 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章