The usual method is to keep all of the thread handles and then wait on each handle. When the handle is signaled, the thread has finished so it is removed from the set of threads. I use std::set<HANDLE>
to keep track of the thread handles. There are two different methods for waiting on multiple objects in Windows:
- Iterate over the set and call
WaitForSingleObject
with a timeout on each one
- Convert the set into an array or vector and call
WaitForMultipleObjects
The first sounds inefficient, but it is actually the most direct and least error prone of the two. If you need to wait for all of the threads, then use the following loop:
std::set<HANDLE> thread_handles; // contains the handle of each worker thread
while (!thread_handles.empty()) {
std::set<HANDLE> threads_left;
for (std::set<HANDLE>::iterator cur_thread=thread_handles.begin(),
last=thread_handles.end();
cur_thread != last; ++cur_thread)
{
DWORD rc = ::WaitForSingleObject(*cur_thread, some_timeout);
if (rc == WAIT_OBJECT_0) {
::CloseHandle(*cur_thread); // necessary with _beginthreadex
} else if (rc == WAIT_TIMEOUT) {
threads_left.add(cur_thread); // wait again
} else {
// this shouldn't happen... try to close the handle and hope
// for the best!
::CloseHandle(*cur_thread); // necessary with _beginthreadex
}
}
std::swap(threads_left, thread_handles);
}
Using WaitForMultipleObjects
to wait for the threads to finish is a bit more difficult than it sounds. The following will wait for all of the threads; however, it only waits for WAIT_MAXIMUM_OBJECTS
threads at a time. Another options is to loop over each page of threads. I'll leave that exercise to the reader ;)
DWORD large_timeout = (5 * 60 * 1000); // five minutes
std::set<HANDLE> thread_handles; // contains the handle of each worker thread
std::vector<HANDLE> ary; // WaitForMultipleObjects wants an array...
while (!thread_handles.empty()) {
ary.assign(thread_handles.begin(), thread_handles.end());
DWORD rc = ::WaitForMultipleObjects(std::min(ary.size(), WAIT_MAXIMUM_OBJECTS),
&ary[0], FALSE, large_timeout);
if (rc == WAIT_FAILED) {
// handle a failure case... this is usually something pretty bad
break;
} else if (rc == WAIT_TIMEOUT) {
// no thread exited in five minutes... this can be tricky since one of
// the threads beyond the first WAIT_MAXIMUM_OBJECTS may have terminated
} else {
long idx = (rc - WAIT_OBJECT_0);
if (idx > 0 && idx < ary.size()) {
// the object at `idx` was signaled, this means that the
// thread has terminated.
thread_handles.erase(ary[idx]);
::CloseHandle(ary[idx]); // necessary with _beginthreadex
}
}
}
This isn't exactly pretty but it should work. If you trust that all of your threads will exit and don't mind waiting for them, then you can use WaitForMultipleObjects(ary.size(), &ary[0], TRUE, INFINITE)
. This usually isn't very safe though since a runaway thread will cause your application to block indefinitely and it will only work if ary.size()
is less than MAXIMUM_WAIT_OBJECTS
.
Of course the other option is to find a thread pool implementation and use it instead. Writing threading code is not really a lot of fun especially once you have to support it in the wild. Consider using something like boost::thread_group
instead.