Обычный способ состоит в том, чтобы сохранить все ручки потока и затем ждать каждого дескриптора. Когда ручка сигнализирована, нить закончила так, что она удалена из набора нитей. Я использую std::set<HANDLE>
для отслеживания ручек потока. Существуют два различных способа ожидания на несколько объектов в Windows:
- итерацию по набору и вызвать
WaitForSingleObject
с тайм-аут на каждом
- Преобразовать в массив или вектор и вызвать
WaitForMultipleObjects
Первые звуки неэффективны, но на самом деле они являются наиболее прямым и наименее подверженным ошибкам двух.Если вам нужно ждать все нити, а затем использовать следующий цикл:
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);
}
Использование WaitForMultipleObjects
ждать резьбы до конца является немного более сложным, чем это кажется. Следующие будут ждать всех потоков; однако он только ждет WAIT_MAXIMUM_OBJECTS
потоков за раз. Другими параметрами являются петля над каждой страницей потоков. Я оставлю это упражнение читателю;)
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
}
}
}
Это не совсем красиво, но оно должно работать. Если вы доверяете, что все ваши потоки выйдут и не возражаете ждать их, вы можете использовать WaitForMultipleObjects(ary.size(), &ary[0], TRUE, INFINITE)
. Это обычно не очень безопасно, хотя, поскольку беглый поток заставит ваше приложение блокировать неограниченное число и, он будет работать, только если ary.size()
меньше MAXIMUM_WAIT_OBJECTS
.
Конечно, другой вариант состоит в том, чтобы найти реализацию пула потоков и использовать его вместо этого. Написание кода потоковой передачи на самом деле не очень весело, особенно если вам придется поддерживать его в дикой природе. Вместо этого используйте вместо этого что-то вроде boost::thread_group
.
Если у вас есть только идентификатор потока, вам нужно будет сначала преобразовать его в HANDLE, вызвав [OpenThread] (https://msdn.microsoft.com/en-us/library/windows/desktop/ms684335 (v = vs .85) .aspx). – rustyx
Нет, не называйте OpenThread! Если поток уже вышел и идентификатор был переназначен, вы могли бы получить дескриптор для другого случайного потока. Используйте _beginthreadex, который возвращает дескриптор потока. (_beginthread также возвращает дескриптор, но (цитируя документы) он «может быть недействительным или указывать на другой поток». Никогда не используйте _beginthread.) – benrg