El método habitual es mantener todas las asas de hilo y luego esperar en cada asa. Cuando el identificador es señalado, el subproceso ha finalizado, por lo que se elimina del conjunto de subprocesos. Yo uso std::set<HANDLE>
para realizar un seguimiento de los identificadores de subprocesos. Hay dos métodos diferentes para la espera en múltiples objetos en Windows:
- iterar sobre el conjunto y llamar
WaitForSingleObject
con un tiempo de espera en cada una
- Convertir el conjunto en una matriz o vector y llamar a
WaitForMultipleObjects
El primero suena ineficiente, pero en realidad es el más directo y el menos propenso a errores de los dos.Si tiene que esperar a que todos los hilos, a continuación, utilizar el siguiente bucle:
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);
}
Usando WaitForMultipleObjects
que esperar a que los hilos a fin es un poco más difícil de lo que parece. Lo siguiente esperará por todos los hilos; sin embargo, solo espera hilos WAIT_MAXIMUM_OBJECTS
a la vez. Otra opción es pasar por cada página de hilos. Dejaré ese ejercicio al lector;)
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
}
}
}
Esto no es exactamente bonito, pero debería funcionar. Si confías en que todos tus hilos se cerrarán y no te molesta esperarlos, entonces puedes usar WaitForMultipleObjects(ary.size(), &ary[0], TRUE, INFINITE)
. Por lo general, esto no es muy seguro, ya que un hilo fuera de control causará que su aplicación bloquee indefinidamente y, solo funcionará si ary.size()
es menor que MAXIMUM_WAIT_OBJECTS
.
Por supuesto, la otra opción es encontrar una implementación de conjunto de subprocesos y usarla en su lugar. Escribir código de enhebrado no es muy divertido, especialmente una vez que tienes que apoyarlo en la naturaleza. Considere usar algo como boost::thread_group
en su lugar.
Si solo tiene una ID de hilo, primero tendrá que convertirla en una HANDLE llamando a [OpenThread] (https://msdn.microsoft.com/en-us/library/windows/desktop/ms684335 (v = vs .85) .aspx). – rustyx
No, ¡no llame a OpenThread! Si el hilo ya salió y la identificación fue reasignada, puede terminar con un control de un hilo aleatorio diferente. Use _beginthreadex, que devuelve un identificador a la cadena. (_beginthread también devuelve un identificador, pero (citando los documentos) "podría no ser válido o apuntar a otro subproceso". Nunca use _beginthread). – benrg