Using the Thread Pool Functions Program Example (Vista/Server 2008)
The following working example creates a custom thread pool, creates a work item and a thread pool timer, and associates them with a cleanup group. The pool consists of one persistent thread. It demonstrates the use of the following thread pool functions:
For the above mentioned functions the following info should be applied.
Minimum supported client: Windows Vista
Minimum supported server: Windows Server 2008
Header: Winbase.h (include Windows.h)
Library: Kernel32.lib
DLL: Kernel32.dll
Create a new empty Win32 console application project. Give a suitable project name and change the project location if needed.
Then, add the source file and give it a suitable name.
Next, add the following source code.
// This just a program skeleton
#include <windows.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
)
{
wprintf(LMyWaitCallback() - Wait callback is over...\n);
// Do something when the wait is over
}
// Thread pool timer callback function template
void CALLBACK MyTimerCallback(
PTP_CALLBACK_INSTANCE Instance,
PVOID Parameter,
PTP_TIMER Timer
)
{
wprintf(LMyTimerCallback() - Timer fireddddd.....\n);
// Do something when the timer fires
}
// This is the thread pool work callback function.
// The callback demonstrates correct behavior when changing the
// state of the thread inside the callback function.
//
// Any changes to the thread state must be restored to original
// before exiting the callback routine.
void CALLBACK MyWorkCallback(
PTP_CALLBACK_INSTANCE Instance,
PVOID Parameter,
PTP_WORK Work
)
{
BOOL bRet = FALSE;
DWORD dwPriorityOriginal = 0;
// Record the original thread priority
dwPriorityOriginal = GetThreadPriority(GetCurrentThread());
if (dwPriorityOriginal == THREAD_PRIORITY_ERROR_RETURN)
{
wprintf(LGetThreadPriority() failed, error is %u\n, GetLastError());
return;
}
// Increase the priority of the thread pool thread
bRet = SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
if (bRet == FALSE)
{
wprintf(LSetThreadPriority() failed, error is %u\n, GetLastError());
return;
}
// Perform tasks at increased priority
{
wprintf(LPerforming task at the higher level of priority...\n);
}
// Restore thread state by resetting the original priority
bRet = SetThreadPriority(GetCurrentThread(), dwPriorityOriginal);
// If restore fails, maybe retry or throw an exception. Otherwise,
// the thread will continue to execute other work items at increased priority.
if (bRet == FALSE)
{
wprintf(LFatal Error! SetThreadPriority() failed, error is %u\n, GetLastError());
return;
}
return;
}
void DemoCleanupPersistentWorkTimer()
{
BOOL bRet = FALSE;
PTP_WORK work = NULL;
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 (pool == NULL)
{
wprintf(LCreateThreadpool() failed, error is %u\n, GetLastError());
goto main_cleanup;
}
// pool creation succeeded
rollback = 1;
// 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 (bRet == FALSE)
{
wprintf(LSetThreadpoolThreadMinimum() failed, error is %u\n, GetLastError());
goto main_cleanup;
}
// Create a cleanup group for this thread pool
cleanupgroup = CreateThreadpoolCleanupGroup();
if (cleanupgroup == NULL)
{
wprintf(LCreateThreadpoolCleanupGroup() failed, error is %u\n, GetLastError());
goto main_cleanup;
}
// Cleanup group creation succeeded
rollback = 2;
// Associate the callback environment with our thread pool
SetThreadpoolCallbackPool(&CallBackEnviron, pool);
// Associate the cleanup group with our thread pool
SetThreadpoolCallbackCleanupGroup(&CallBackEnviron, cleanupgroup, NULL);
// Create work with the callback environment
work = CreateThreadpoolWork(workcallback, NULL, &CallBackEnviron);
if (work == NULL)
{
wprintf(LCreateThreadpoolWork() failed, error is %u\n, GetLastError());
goto main_cleanup;
}
// Creation of work succeeded
rollback = 3;
// 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 (timer == NULL)
{
wprintf(LCreateThreadpoolTimer() failed, error is %u\n, GetLastError());
goto main_cleanup;
}
// Timer creation succeeded
rollback = 4;
// Set the timer to fire in one second
ulDueTime.QuadPart = (LONGLONG) -(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 calls the cleanup
// functions for all the individual objects in the specified
// cleanup group.
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 (hEvent == NULL)
{
// Error Handling
wprintf(LCreateEvent() failed, error is %u\n, GetLastError());
return;
}
// CreateEvent() succeeded
rollback = 1;
Wait = CreateThreadpoolWait(waitcallback, NULL, NULL);
if(Wait == NULL)
{
wprintf(LCreateThreadpoolWait() failed, error is %u\n, GetLastError());
goto new_wait_cleanup;
}
// CreateThreadpoolWait succeeded
rollback = 2;
// 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 wait
CloseThreadpoolWait(Wait);
case 1:
// Close event
CloseHandle(hEvent);
default:
break;
}
return;
}
int wmain(int argc, WCHAR **argv)
{
DemoNewRegisterWait();
DemoCleanupPersistentWorkTimer();
return 0;
}
Build and run the project. The following is the sample output when run on Windows XP Pro SP2. We need Windows Vista/Windows Server 2008.
The following section is left for you to be explored: Process and Thread Reference.