// PerfTest.cpp : This file contains the 'main' function. Program execution begins and ends there. // #include <windows.h> #include <tchar.h> #include <stdio.h> #include <thread> #include <chrono> // // 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; thread_local int idx = 0; idx++; int sum = 0; for (int i = 0; i < 1000000; i++) { sum += i; } // // Do something when the work callback is invoked. // { _tprintf(_T("MyWorkCallback: Task performed. sum: %d, idx: %d\n"), sum, idx); } 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 (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, 5); 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 for (int i = 0; i < 1000000000; i++) { // // Submit the work to the pool. Because this was a pre-allocated // work item (using CreateThreadpoolWork), it is guaranteed to execute. // SubmitThreadpoolWork(work); std::this_thread::sleep_for(std::chrono::nanoseconds(100)); } // // 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; } int main(void) { DemoCleanupPersistentWorkTimer(); return 0; }
上面是测试用的代码,我测试了如下两种情况:
(1)std::this_thread::sleep_for(std::chrono::nanoseconds(100));
(2)std::this_thread::sleep_for(std::chrono::nanoseconds(101));
测试使用的IDE是VS2022,如下图所示
测试的是release编译,使用Start Without Debugging启动。根据测试结果,使用100纳秒的sleep时间,CPU占用大约为14%到22%,而使用101纳秒的sleep时间,CPU占用为0%到4%,其中大多数时间显示为0%(表示低于1%)。暂时没有调查具体原因,先记录下来。如果您对这个原因有想法,欢迎指教。