Windows 核心编程 11 windows 线程池

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 
#include 
#include 


// C RunTime Header Files
#include 
#include 
#include 
#include 
#include 

#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 
#include 


//


// 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 
#include 
#include 
#include 

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 
#include 
#include 

//
// 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;
}



你可能感兴趣的:(windows,核心编程读书笔记)