Windows 线程池
本节内容有:
1 以异步方式调用函数
windows 线程池新的线程池函数有异步方式的函数原型
VOID NTAPI SimpleCallBack(PTP_CALLBACK_INSTANCE pInstance,PVOID pvContext);
提交一个请求 调用成功返回TRUE,失败返回FALSE
BOOL TrySubmitThreadpoolCallback(PTP_SIMPLE_CALLBACK pfnCallBack,PVOID pvContext,PTP_CALLBACK_ENVIRON pcbe);
创建一个工作项
PTP_WORD
CreateThreadpoolWork(PTP_WORD_CALLBACK pfnwordHandler,PVOID PVcontxt,PTP_callBack_Environ pcbe);
工作项的回调函数
VOID CallBack wordCallBack(PTP_CALLBACK_INSTANCE Instance,PVOID Context,ptp_word word);
向线程池提交一个请求void SubmitThreadpoolword(PTP_WORD pword);
取消已经提交的工作项或者将自己挂起void WaitforThreadpoolWordCallbacks(PTP_WORD Pword,BOOL bCancelPendingCallbacks);
不需要一个工作项时调用
void CloseThreadpoolWord(PTP_WORD pwk);
异步函数的代码在最下面
2 每隔一段时间调用一个函数
有时应用程序需要在某些时间执行某些任务,windos 提供了可等待的计时器内核对象,它使我们很容易就能够得到一个基于时间的通知。
我们可以使用线程池函数来管理可等待计时器,包括重置和等待下一次触发。
为了将一个工作项安排在某个时间执行,我们必须定义一个回调函数,函数声明如下:
VOID CALLBACK TimeoutCallBack(PTP_CALLBACK_INSTANCE pInstance, 回调函数终止操作讲解
PVOID pvContext, 下面函数传过来的
PTP_TIMER pTimer); 下面函数的返回值
然后调用下面的函数通知线程池应该在何时调用我们的函数:
PTP_TIMER
CreateThreadpoolTimer(
PTP_TIMER_CALLBACK pfnTimerCallBack,
PVOID pvcontext,
PTP_CALLBACK_ENVIRON pcbe);
第一个参数是函数指针,第2个参数是传递给回调函数的值,参数3 后面讲
当我们想要向线程池注册计时器的时候,应该调用
SetThreadpoolTimer函数:
VOIDWINAPISetThreadpoolTimer(
__inout PTP_TIMER pti,
__in_opt PFILETIME pftDueTime,
__in DWORD msPeriod,
__in_opt DWORD msWindowLength );
参数 pti 用来标识CreateThreadpoolTimer返回的TP_TIMER 对象.
参数ptfDueTime 表示第一次调用回调函数应该是什么时候,我们可以传一个负值(微妙为单位)来指定相对时间. 该时间相对于SetThreadpoolTimer的时间,-1是一个特例,表示
立即开始。 为了指定一个绝对时间,应该为正值,100纳秒为单位,从1600年1月1日算起.
参数 msPeriod 表示如果只想触发一次,传0即可,如果不传0,单位是微秒。表示第一次调用以后,以后按这个时间循环调用.
参数 mswindoLength 表示增加一个随机性,如果有多个定时器也冲突。
在设置了计时器之后,我们还可以通过调用SetThreadpoolTimer 并在pti参数中传入以前设置的计时器指针,来对已有的计时器修改
我们传入pftDueTime 为NULL时,这等于告诉线程池停止调用我们的TimerCallBack函数.这不失为一种将计时器暂停但又不必销毁计时器对象的好办法,尤其在回调函数内部,更是如此。
IsThreadpoolTimerSet 来确定某个计时器是否被设置(pftDuetime不为NULL);
WINBASEAPI BOOL WINAPI IsThreadpoolTimerSet( __inout PTP_TIMER pti )
来让线程等待一个定时器完成调用下面函数
WaitForThreadpoolTimerCallbacks();
CloseThreadpoolTimer();函数来释放计时器的内存资源,他们的前面介绍的都一样,如异步函数调用.
下面介绍线程池计时器的使用:见最下 Timed Message Box
3 在内核对象触发时调用一个函数
首先编写一个符合下面的函数原型:
VOID CALLBACK WaitCallback(PTP_CALLBACK_INSTANCE pInstance,PTP_WAIT Wait,TP_WAIT_RESULT WaitResult);】
然后通过调用CreateThreadpoolWait
来创建一个线程池等待对象
PTP_WAIT WINAPI
CreateThreadpoolWait( __in PTP_WAIT_CALLBACK pfnwa,
__inout_opt PVOID pv,
__in_opt PTP_CALLBACK_ENVIRON pcbe );
当我们创建完成后,我们调用下面的函数来将一个内核对象绑定到这个线程池:
VOID WINAPI SetThreadpoolWait(
__inout PTP_WAIT pwa, 创建一个等待对象
__in_opt HANDLE h, 需要等待的内核对象
__in_opt PFILETIME pftTimeout 表示最长花多少时间等待 0表示不等待,负值表示相对时间
正值表示绝对时间,NULL表示一直等下去 );
当内核对象触发或者等待超时,那么线程池中的某个线程会调用我们的回调函数,返回值有3个类型。
WAIT_OBJECT_0
WAIT_TIMEOUT
WAIT_ABANDONED_0
waitForThreadpoolWaitCallbacks函数来等待一个等待项完成
CloseThreadpoolWait函数来释放一个等待项
线程池的内核对象触发代码在最下面:
4 在异步IO请求完成时调用一个函数
CreateThreadpoolIo来创建一个IO线程池
5 回调函数的终止操作
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Windows 核心编程全部源代码在这个链接上:http://download.csdn.net/detail/woleiwei/5716243
//异步函数的代码
第11章 代码 11-Batch
/******************************************************************************
Module: Batch.cpp
Notices: Copyright (c) 2008 Jeffrey Richter & Christophe Nasarre
******************************************************************************/
#include "..\CommonFiles\CmnHdr.h" /* See Appendix A. */
#include <Windowsx.h>
#include <WinBase.h>
#include <WinNT.h>
// C RunTime Header Files
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <tchar.h>
#include <strsafe.h>
#include "Batch.h"
//////////////////////////////////////////////////////////////////////////////
// Global variables
HWND g_hDlg = NULL; //窗口句柄
PTP_WORK g_pWorkItem = NULL; //工作项
volatile LONG g_nCurrentTask = 0; //当前工作项数量
// Global definitions
#define WM_APP_COMPLETED (WM_APP+123)
//////////////////////////////////////////////////////////////////////////////
void AddMessage(LPCTSTR szMsg) {
HWND hListBox = GetDlgItem(g_hDlg, IDC_LB_STATUS);
ListBox_SetCurSel(hListBox, ListBox_AddString(hListBox, szMsg));
}
//////////////////////////////////////////////////////////////////////////////
void NTAPI TaskHandler(PTP_CALLBACK_INSTANCE Instance, PVOID Context, PTP_WORK Work) {
//返回值是增加后的值
LONG currentTask = InterlockedIncrement(&g_nCurrentTask);
TCHAR szMsg[MAX_PATH];
StringCchPrintf(
szMsg, _countof(szMsg),
TEXT("[%u] Task #%u is starting."), GetCurrentThreadId(), currentTask);
AddMessage(szMsg);
// Simulate a lot of work
Sleep(currentTask * 1000);
StringCchPrintf(
szMsg, _countof(szMsg),
TEXT("[%u] Task #%u is done."), GetCurrentThreadId(), currentTask);
AddMessage(szMsg);
//返回值是减少后的值 最后一个处理完毕了
if (InterlockedDecrement(&g_nCurrentTask) == 0)
{
// Notify the UI thread for completion.
PostMessage(g_hDlg, WM_APP_COMPLETED, 0, (LPARAM)currentTask);
}
}
//////////////////////////////////////////////////////////////////////////////
void OnStartBatch() {
// Disable Start button
Button_Enable(GetDlgItem(g_hDlg, IDC_BTN_START_BATCH), FALSE);
AddMessage(TEXT("----Start a new batch----"));
// Submit 4 tasks by using the same work item
//提交一个工作项用于4个任务
SubmitThreadpoolWork(g_pWorkItem);
SubmitThreadpoolWork(g_pWorkItem);
SubmitThreadpoolWork(g_pWorkItem);
SubmitThreadpoolWork(g_pWorkItem);
AddMessage(TEXT("4 tasks are submitted."));
}
//////////////////////////////////////////////////////////////////////////////
void Dlg_OnCommand(HWND hWnd, int id, HWND hWndCtl, UINT codeNotify) {
switch (id) {
case IDOK:
case IDCANCEL:
EndDialog(hWnd, id);
break;
case IDC_BTN_START_BATCH:
OnStartBatch();
break;
}
}
BOOL Dlg_OnInitDialog(HWND hWnd, HWND hWndFocus, LPARAM lParam) {
// Keep track of main dialog window for error messages
g_hDlg = hWnd;
return(TRUE);
}
//////////////////////////////////////////////////////////////////////////////
INT_PTR WINAPI Dlg_Proc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
chHANDLE_DLGMSG(hWnd, WM_INITDIALOG, Dlg_OnInitDialog);
chHANDLE_DLGMSG(hWnd, WM_COMMAND, Dlg_OnCommand);
case WM_APP_COMPLETED: {
TCHAR szMsg[MAX_PATH+1];
StringCchPrintf(
szMsg, _countof(szMsg),
TEXT("____Task #%u was the last task of the batch____"), lParam);
AddMessage(szMsg);
// Don't forget to enable the button
Button_Enable(GetDlgItem(hWnd, IDC_BTN_START_BATCH), TRUE);
}
break;
}
return(FALSE);
}
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE, LPTSTR pCmdLine, int) {
// Create the work item that will be used by all tasks
//创建一个工作项用于所有的任务
g_pWorkItem = CreateThreadpoolWork(TaskHandler, NULL, NULL);//参数1 表示回调函数
if (g_pWorkItem == NULL) {
MessageBox(NULL, TEXT("Impossible to create the work item for tasks."),
TEXT(""), MB_ICONSTOP);
return(-1);
}
DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, Dlg_Proc,
_ttoi(pCmdLine));
// Don't forget to delete the work item
CloseThreadpoolWork(g_pWorkItem);
return(0);
}
//////////////////////////////// End of File /////////////////////////////////
下面代码是 定时器线程池对象
/******************************************************************************
Module: TimedMsgBox.cpp
Notices: Copyright (c) 2008 Jeffrey Richter & Christophe Nasarre
******************************************************************************/
#include "..\CommonFiles\CmnHdr.h" /* See Appendix A. */
#include <tchar.h>
#include <StrSafe.h>
//////////////////////////////////////////////////////////////////////////////
// The caption of our message box
TCHAR g_szCaption[100];
// How many seconds we'll display the message box
int g_nSecLeft = 0;
// This is STATIC window control ID for a message box
// 这个ID是 spy++查看到的
#define ID_MSGBOX_STATIC_TEXT 0x0000ffff
//////////////////////////////////////////////////////////////////////////////
//定时器回调函数
VOID CALLBACK MsgBoxTimeoutCallback(
PTP_CALLBACK_INSTANCE pInstance,
PVOID pvContext,
PTP_TIMER pTimer
)
{
// NOTE: Due to a thread race condition, it is possible (but very unlikely)
// that the message box will not be created when we get here.
//通过标题获得窗口句柄
HWND hwnd = FindWindow(NULL, g_szCaption);
if (hwnd != NULL) {
if (g_nSecLeft == 1) {
// The time is up; force the message box to exit.
EndDialog(hwnd, IDOK);
return;
}
// The window does exist; update the time remaining.
TCHAR szMsg[100];
StringCchPrintf(szMsg, _countof(szMsg),
TEXT("You have %d seconds to respond"), --g_nSecLeft);
SetDlgItemText(hwnd, ID_MSGBOX_STATIC_TEXT, szMsg);
} else {
// The window does not exist yet; do nothing this time.
// We'll try again in another second.
}
}
int WINAPI _tWinMain(HINSTANCE, HINSTANCE, PTSTR, int) {
_tcscpy_s(g_szCaption, _countof(g_szCaption), TEXT("Timed Message Box"));
// How many seconds we'll give the user to respond
g_nSecLeft = 10;
// Create the threadpool timer object
PTP_TIMER lpTimer =
CreateThreadpoolTimer(MsgBoxTimeoutCallback, NULL, NULL); //第一个参数是 回调函数在前面定义了,后面2个参数没有使用到
if (lpTimer == NULL) {
TCHAR szMsg[MAX_PATH];
StringCchPrintf(szMsg, _countof(szMsg),
TEXT("Impossible to create the timer: %u"), GetLastError());
MessageBox(NULL, szMsg, TEXT("Error"), MB_OK | MB_ICONERROR);
return(-1);
}
// Start the timer in one second to trigger every 1 second
ULARGE_INTEGER ulRelativeStartTime;
ulRelativeStartTime.QuadPart = (LONGLONG) -(10000000); // start in 1 second 100纳秒为单位 那么 1000 000 0表示1秒
FILETIME ftRelativeStartTime;
ftRelativeStartTime.dwHighDateTime = ulRelativeStartTime.HighPart;
ftRelativeStartTime.dwLowDateTime = ulRelativeStartTime.LowPart;
SetThreadpoolTimer(
lpTimer,
&ftRelativeStartTime, // 一个单位表示100纳秒 表示调用这个函数以后,回调函数多久第一次执行
1000, // Triggers every 1000 milliseconds 每1秒触发一次
0
);
MessageBox(NULL, TEXT("You have 10 seconds to respond"),
g_szCaption, MB_OK);
// Clean up the timer
CloseThreadpoolTimer(lpTimer);//如果不关闭线程池对象,那么回调函数会继续运行,直到进程退出
// Let us know if the user responded or if we timed out
MessageBox(
NULL, (g_nSecLeft == 1) ? TEXT("Timeout") : TEXT("User responded"),
TEXT("Result"), MB_OK);
return(0);
}
//////////////////////////////// End of File /////////////////////////////////
3 内核对象线程池函数
#include <iostream>
#include <windows.h>
#include <Winbase.h>
#include <tchar.h>
VOID CALLBACK MyWaitCallback(
PTP_CALLBACK_INSTANCE Instance,
PVOID Parameter,
PTP_WAIT Wait,
TP_WAIT_RESULT WaitResult
)
{
// Instance, Parameter, Wait, and WaitResult not used in this example.
UNREFERENCED_PARAMETER(Instance);
UNREFERENCED_PARAMETER(Parameter);
UNREFERENCED_PARAMETER(Wait);
UNREFERENCED_PARAMETER(WaitResult);
//
// Do something when the wait is over.
//
_tprintf(_T("MyWaitCallback: wait is over.\n"));
}
VOID DemoNewRegisterWait()
{
PTP_WAIT Wait = NULL;
PTP_WAIT_CALLBACK waitcallback = MyWaitCallback;
HANDLE hEvent = NULL;
UINT i = 0;
UINT rollback = 0;
//
// Create an auto-reset event.
//
hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (NULL == hEvent) {
// Error Handling
return;
}
rollback = 1; // CreateEvent succeeded
Wait = CreateThreadpoolWait(waitcallback,
NULL,
NULL);
if(NULL == Wait) {
_tprintf(_T("CreateThreadpoolWait failed. LastError: %u\n"),
GetLastError());
goto new_wait_cleanup;
}
rollback = 2; // CreateThreadpoolWait succeeded
//
// Need to re-register the event with the wait object
// each time before signaling the event to trigger the wait callback.
//
for (i = 0; i < 5; i ++) {
SetThreadpoolWait(Wait,
hEvent,
NULL);// 参数3 表示花多少时间等待 参数2的内核对象触发
SetEvent(hEvent);
//
// Delay for the waiter thread to act if necessary.
//
Sleep(500);
//
// Block here until the callback function is done executing.
//
//表示一直等待工作项完成为止
WaitForThreadpoolWaitCallbacks(Wait, FALSE);
}
new_wait_cleanup:
switch (rollback) {
case 2:
// Unregister the wait by setting the event to NULL.
SetThreadpoolWait(Wait, NULL, NULL);//取消等待项和事件关联关系
// Close the wait.
CloseThreadpoolWait(Wait);
case 1:
// Close the event.
CloseHandle(hEvent);
default:
break;
}
return;
}
int main( void)
{
DemoNewRegisterWait();
return 0;
}
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
//
// Thread pool wait callback function template
//
VOID
CALLBACK
MyWaitCallback(
PTP_CALLBACK_INSTANCE Instance,
PVOID Parameter,
PTP_WAIT Wait,
TP_WAIT_RESULT WaitResult
)
{
// Instance, Parameter, Wait, and WaitResult not used in this example.
UNREFERENCED_PARAMETER(Instance);
UNREFERENCED_PARAMETER(Parameter);
UNREFERENCED_PARAMETER(Wait);
UNREFERENCED_PARAMETER(WaitResult);
//
// Do something when the wait is over.
//
_tprintf(_T("MyWaitCallback: wait is over.\n"));
}
//
// Thread pool timer callback function template
//
VOID
CALLBACK
MyTimerCallback(
PTP_CALLBACK_INSTANCE Instance,
PVOID Parameter,
PTP_TIMER Timer
)
{
// Instance, Parameter, and Timer not used in this example.
UNREFERENCED_PARAMETER(Instance);
UNREFERENCED_PARAMETER(Parameter);
UNREFERENCED_PARAMETER(Timer);
//
// Do something when the timer fires.
//
_tprintf(_T("MyTimerCallback: timer has fired.\n"));
}
//
// This is the thread pool work callback function.
//
VOID
CALLBACK
MyWorkCallback(
PTP_CALLBACK_INSTANCE Instance,
PVOID Parameter,
PTP_WORK Work
)
{
// Instance, Parameter, and Work not used in this example.
UNREFERENCED_PARAMETER(Instance);
UNREFERENCED_PARAMETER(Parameter);
UNREFERENCED_PARAMETER(Work);
BOOL bRet = FALSE;
//
// Do something when the work callback is invoked.
//
{
_tprintf(_T("MyWorkCallback: Task performed.\n"));
}
return;
}
VOID
DemoCleanupPersistentWorkTimer()
{
BOOL bRet = FALSE;
PTP_WORK work = NULL; // a word Item
PTP_TIMER timer = NULL;
PTP_POOL pool = NULL;
PTP_WORK_CALLBACK workcallback = MyWorkCallback;
PTP_TIMER_CALLBACK timercallback = MyTimerCallback;
//回调函数环境变量
TP_CALLBACK_ENVIRON CallBackEnviron;
PTP_CLEANUP_GROUP cleanupgroup = NULL;
FILETIME FileDueTime;
ULARGE_INTEGER ulDueTime;
UINT rollback = 0;
//初始化线程池环境
InitializeThreadpoolEnvironment(&CallBackEnviron);
//
// Create a custom, dedicated(专业) thread pool.
//
pool = CreateThreadpool(NULL);
if (NULL == pool) {
_tprintf(_T("CreateThreadpool failed. LastError: %u\n"),
GetLastError());
goto main_cleanup;
}
rollback = 1; // pool creation succeeded
//
// The thread pool is made persistent(固有的) simply by setting
// both the minimum and maximum threads to 1.
//
SetThreadpoolThreadMaximum(pool, 1);
bRet = SetThreadpoolThreadMinimum(pool, 1);
if (FALSE == bRet) {
_tprintf(_T("SetThreadpoolThreadMinimum failed. LastError: %u\n"),
GetLastError());
goto main_cleanup;
}
//
// Create a cleanup group for this thread pool. 给这个线程池创建一个清理组
//
cleanupgroup = CreateThreadpoolCleanupGroup();
if (NULL == cleanupgroup) {
_tprintf(_T("CreateThreadpoolCleanupGroup failed. LastError: %u\n"),
GetLastError());
goto main_cleanup;
}
rollback = 2; // Cleanup group creation succeeded
//
// Associate(关联) the callback environment with our thread pool.
//
SetThreadpoolCallbackPool(&CallBackEnviron, pool);
//
// Associate the cleanup group with our thread pool.
// Objects created with the same callback environment
// as the cleanup group become members of the cleanup group.
//
SetThreadpoolCallbackCleanupGroup(&CallBackEnviron,
cleanupgroup,
NULL);
//
// Create work with the callback environment.
//
work = CreateThreadpoolWork(workcallback,
NULL,
&CallBackEnviron);
if (NULL == work) {
_tprintf(_T("CreateThreadpoolWork failed. LastError: %u\n"),
GetLastError());
goto main_cleanup;
}
rollback = 3; // Creation of work succeeded
//
// Submit the work to the pool. Because this was a pre-allocated
// work item (using CreateThreadpoolWork), it is guaranteed to execute.
//
SubmitThreadpoolWork(work);
//
// Create a timer with the same callback environment.
//
timer = CreateThreadpoolTimer(timercallback,
NULL,
&CallBackEnviron);
if (NULL == timer) {
_tprintf(_T("CreateThreadpoolTimer failed. LastError: %u\n"),
GetLastError());
goto main_cleanup;
}
rollback = 4; // Timer creation succeeded
//
// Set the timer to fire in one second.
//
ulDueTime.QuadPart = (ULONGLONG) -(1 * 10 * 1000 * 1000);
FileDueTime.dwHighDateTime = ulDueTime.HighPart;
FileDueTime.dwLowDateTime = ulDueTime.LowPart;
SetThreadpoolTimer(timer,
&FileDueTime,
0,
0);
//
// Delay(延迟) for the timer to be fired
//
Sleep(1500);
//
// Wait for all callbacks to finish.
// CloseThreadpoolCleanupGroupMembers also releases objects
// that are members of the cleanup group, so it is not necessary (不必要的)
// to call close functions on individual objects
// after calling CloseThreadpoolCleanupGroupMembers.
//
CloseThreadpoolCleanupGroupMembers(cleanupgroup,
FALSE,
NULL);
//
// Already cleaned up the work item with the
// CloseThreadpoolCleanupGroupMembers, so set rollback to 2.
//
rollback = 2;
goto main_cleanup;
main_cleanup:
//
// Clean up any individual pieces manually
// Notice the fall-through structure of the switch.
// Clean up in reverse order.
//
switch (rollback) {
case 4:
case 3:
// Clean up the cleanup group members.
CloseThreadpoolCleanupGroupMembers(cleanupgroup,
FALSE, NULL);
case 2:
// Clean up the cleanup group.
CloseThreadpoolCleanupGroup(cleanupgroup);
case 1:
// Clean up the pool.
CloseThreadpool(pool);
default:
break;
}
return;
}
VOID
DemoNewRegisterWait()
{
PTP_WAIT Wait = NULL;
PTP_WAIT_CALLBACK waitcallback = MyWaitCallback;
HANDLE hEvent = NULL;
UINT i = 0;
UINT rollback = 0;
//
// Create an auto-reset event.
//
hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (NULL == hEvent) {
// Error Handling
return;
}
rollback = 1; // CreateEvent succeeded
Wait = CreateThreadpoolWait(waitcallback,
NULL,
NULL);
if(NULL == Wait) {
_tprintf(_T("CreateThreadpoolWait failed. LastError: %u\n"),
GetLastError());
goto new_wait_cleanup;
}
rollback = 2; // CreateThreadpoolWait succeeded
//
// Need to re-register the event with the wait object
// each time before signaling the event to trigger the wait callback.
//
for (i = 0; i < 5; i ++) {
SetThreadpoolWait(Wait,
hEvent,
NULL);
SetEvent(hEvent);
//
// Delay for the waiter thread to act if necessary.
//
Sleep(500);
//
// Block here until the callback function is done executing.
//
WaitForThreadpoolWaitCallbacks(Wait, FALSE);
}
new_wait_cleanup:
switch (rollback) {
case 2:
// Unregister the wait by setting the event to NULL.
SetThreadpoolWait(Wait, NULL, NULL);
// Close the wait.
CloseThreadpoolWait(Wait);
case 1:
// Close the event.
CloseHandle(hEvent);
default:
break;
}
return;
}
int main( void)
{
DemoNewRegisterWait();
DemoCleanupPersistentWorkTimer();
getchar();
return 0;
}