Keeping track of all threads in a thread pool

Publish date: 2024-08-29

I am looking at using the Windows Threading API and the issue it seems to have is you cannot keep track of when all the threads are completed. You can keep track of when the work item has been completed, assuming you kept track of each one. From my research there is no direct way to query the thread pool to see if the work items submitted has all be completed.

#include <windows.h> #include <tchar.h> #include <stdio.h> VOID CALLBACK MyWorkCallback(PTP_CALLBACK_INSTANCE Instance, PVOID Parameter, PTP_WORK Work) { DWORD threadId = GetCurrentThreadId(); BOOL bRet = FALSE; printf("%d thread\n", threadId); return; } int main() { TP_CALLBACK_ENVIRON CallBackEnviron; PTP_POOL pool = NULL; PTP_CLEANUP_GROUP cleanupgroup = NULL; PTP_WORK_CALLBACK workcallback = MyWorkCallback; PTP_TIMER timer = NULL; PTP_WORK work = NULL; InitializeThreadpoolEnvironment(&CallBackEnviron); pool = CreateThreadpool(NULL); SetThreadpoolThreadMaximum(pool, 1); SetThreadpoolThreadMinimum(pool, 3); SetThreadpoolCallbackPool(&CallBackEnviron, pool); for (int i = 0; i < 10; ++i) { work = CreateThreadpoolWork(workcallback, NULL, &CallBackEnviron); SubmitThreadpoolWork(work); WaitForThreadpoolWorkCallbacks(work, FALSE); // This waits for the work item to get completed. } return 1; } 

Here is a simple example. What happens is on the WaitForThreadpoolWorkCallbacks I am able to wait on that specific work item. Which is no problem if I am doing a few things. However, if I am traversing a directory and have thousands of files that I need to have work done on them, I don't want to keep track of each individual work item. Is it possible to query the Thread Pool queue to see if anything is left for processing? Or to find out if any of the threads are still working?

4

1 Answer

you need keep track of active tasks ( like pendcnt in comment) +1. but this must not be global variable, but member in some struct. and pass pointer to this struct to work item. increment this counter before call SubmitThreadpoolWork and decrement from callback, before exit. but you also need and event - set this event in signal state, when counter became 0. and wait on event from main thread. if your code inside dll, which can be unloaded - you need also reference dll, before SubmitThreadpoolWork and FreeLibraryWhenCallbackReturns from callback. also important that counter value - was 1 (not 0) ininitally - so this is count_of_active_cb + 1, and decrement it before begin wait (if not do this - counter can became 0 early - for instance first callback exit before you activate second)

class Task { HANDLE _hEvent = 0; ULONG _dwThreadId = 0; LONG _dwRefCount = 1; public: ~Task() { if (_hEvent) CloseHandle(_hEvent); } ULONG Init() { if (HANDLE hEvent = CreateEvent(0, 0, 0, 0)) { _hEvent = hEvent; return NOERROR; } return GetLastError(); } void AddTask() { InterlockedIncrementNoFence(&_dwRefCount); } void EndTask() { if (!InterlockedDecrement(&_dwRefCount)) { if (_dwThreadId != GetCurrentThreadId()) { if (!SetEvent(_hEvent)) __debugbreak(); } } } void Wait() { _dwThreadId = GetCurrentThreadId(); EndTask(); if (_dwRefCount && WaitForSingleObject(_hEvent, INFINITE) != WAIT_OBJECT_0) __debugbreak(); } }; VOID CALLBACK MyWorkCallback(PTP_CALLBACK_INSTANCE Instance, PVOID Parameter, PTP_WORK /*Work*/) { // need only if your code in dll which can be unloaded FreeLibraryWhenCallbackReturns(Instance, (HMODULE)&__ImageBase); WCHAR sz[32]; swprintf_s(sz, _countof(sz), L"[%x] thread", GetCurrentThreadId()); MessageBoxW(0, 0, sz, MB_ICONINFORMATION); reinterpret_cast<Task*>(Parameter)->EndTask(); } void CbDemo() { Task task; if (task.Init() == NOERROR) { ULONG n = 2; do { if (PTP_WORK pwk = CreateThreadpoolWork(MyWorkCallback, &task, 0)) { HMODULE hmod; // need only if your code in dll which can be unloaded if (GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (PWSTR)&__ImageBase, &hmod)) { task.AddTask(); SubmitThreadpoolWork(pwk); } CloseThreadpoolWork(pwk); } } while (--n); MessageBoxW(0, 0, L"Main Thread", MB_ICONWARNING); task.Wait(); __nop(); } } 

ncG1vNJzZmirpJawrLvVnqmfpJ%2Bse6S7zGiorp2jqbawutJobXFxaWV%2BeoOOpJyeqJmjtG7A0ZqapGWfm3qiuMtmq6GqlZaxtHnIp2SaZaSdv6atw2anqKec