2012-03-30 9 views
18

¿Es posible establecer un tiempo de espera para una llamada al std::thread::join()? Quiero manejar el caso en el que el hilo tarda demasiado en ejecutarse o terminar el hilo. Puedo estar haciendo esto para múltiples hilos (por ejemplo, hasta 30).Tiempo de espera para thread.join()

Preferiblemente sin impulso, pero estaría interesado en una solución de impulso si esa es la mejor manera.

+0

¿Qué te gustaría que ocurra cuando se acabe el tiempo? ¿Se terminará el hilo? –

+4

Esto es lo que 'std :: thread :: native_handle' es para - use las funciones del sistema operativo para hacer una unión temporizada. – ildjarn

Respuesta

13

No hay tiempo de espera para std::thread::join(). Sin embargo, puede ver std::thread::join() como una mera función de conveniencia. Con condition_variable s puede crear una comunicación y cooperación muy rica entre sus hilos, incluidas las esperas temporizadas. Por ejemplo:

#include <chrono> 
#include <thread> 
#include <iostream> 

int thread_count = 0; 
bool time_to_quit = false; 
std::mutex m; 
std::condition_variable cv; 

void f(int id) 
{ 
    { 
    std::lock_guard<std::mutex> _(m); 
    ++thread_count; 
    } 
    while (true) 
    { 
     { 
     std::lock_guard<std::mutex> _(m); 
     std::cout << "thread " << id << " working\n"; 
     } 
     std::this_thread::sleep_for(std::chrono::milliseconds(250)); 
     std::lock_guard<std::mutex> _(m); 
     if (time_to_quit) 
      break; 
    } 
    std::lock_guard<std::mutex> _(m); 
    std::cout << "thread ended\n"; 
    --thread_count; 
    cv.notify_all(); 
} 

int main() 
{ 
    typedef std::chrono::steady_clock Clock; 
    std::thread(f, 1).detach(); 
    std::thread(f, 2).detach(); 
    std::thread(f, 3).detach(); 
    std::thread(f, 4).detach(); 
    std::thread(f, 5).detach(); 
    auto t0 = Clock::now(); 
    auto t1 = t0 + std::chrono::seconds(5); 
    std::unique_lock<std::mutex> lk(m); 
    while (!time_to_quit && Clock::now() < t1) 
     cv.wait_until(lk, t1); 
    time_to_quit = true; 
    std::cout << "main ending\n"; 
    while (thread_count > 0) 
     cv.wait(lk); 
    std::cout << "main ended\n"; 
} 

En este ejemplo main lanza varios hilos para hacer el trabajo, todos los cuales comprueban de vez en cuando si es el momento de dejar de fumar bajo un mutex (esto también podría ser un atómica). El hilo principal también monitorea si es el momento de abandonar (si los hilos hacen todo su trabajo). Si se agota la paciencia de main, simplemente declara que es hora de abandonar, luego espera a que todos los hilos realicen la limpieza necesaria antes de salir.

+1

Esto no es del todo correcto, ya que 'thread_count' necesita ser atómico, ¿no es así? –

+1

@MooingDuck: No en este caso porque en todas partes 'thread_count' se lee o se escribe bajo el estado bloqueado de' m'. Entonces, hacer 'thread_count' atomic sería correcto, pero redundante e innecesariamente costoso. –

+0

En realidad, lo que me llamó la atención fue 'while (thread_count> 0) cv.wait (lk);'. ¿No es esa una de esas cosillas que permite caché 'thread_count' y no funciona" como esperas "? –

3

timed_join() ahora está en desuso. Utilice try_join_for() en su lugar:

myThread.try_join_for(boost::chrono::milliseconds(8000)) 
4

Sí, es posible. La solución que se ha sugerido por Galik se ve así:

#include <thread> 
#include <future> 
... 
// Launch the thread. 
std::thread thread(ThreadFnc, ...); 
... 
// Terminate the thread. 
auto future = std::async(std::launch::async, &std::thread::join, &thread); 
if (future.wait_for(std::chrono::seconds(5)) 
    == std::future_status::timeout) { 

    /* --- Do something, if thread has not terminated within 5 s. --- */ 

} 

Sin embargo, esto presenta esencialmente un tercer hilo que realiza la thread.join().

. (Nota: El destructor de future se bloqueará hasta thread se ha unido y el hilo auxiliar ha terminado)


Tal vez el lanzamiento de un hilo sólo para traer otro hilo hacia abajo no es lo que desea. Hay otro, solución portátil sin un hilo auxiliar:

#include <thread> 
#include <future> 
... 
// Launch the thread. 
std::future<T_return>* hThread 
    = new std::future<T_return>(std::async(std::launch::async, ThreadFnc, ...)); 
... 
// Terminate the thread. 
if (hThread->wait_for(std::chrono::seconds(5)) 
    == std::future_status::timeout) { 

    /* --- Do something, if thread has not terminated within 5 s. --- */ 

} else 
    delete hThread; 

donde T_return es el tipo de retorno de su procedimiento hilo. Este escenario utiliza una combinación de std::future/std::async en lugar de std::thread.

Tenga en cuenta que hThread es un puntero. Cuando llame al operador delete, invocará el destructor de *hThread y bloqueará hasta que el hilo haya finalizado.

He probado ambas versiones con gcc 4.9.3 en Cygwin.

+1

Buen enfoque, pero tengo que esperar varios hilos. (Uso std :: thread threads [nrOfThreads]) ¿Es la primera solución aplicable en un ciclo de bucles over threads? ¿Y qué debo hacer si el tiempo de espera excede? Thx – leon22

+0

La primera mitad es una buena solución, pero la segunda mitad no tiene sentido, además de su mal estilo: ¿Por qué no usa un futuro si puede poner su funcionalidad de hilos en una función con semántica futura? ? ¿Por qué el puntero simple con nuevo y eliminar? Obviamente estaban hablando de> = C++ 11, por lo que este enfoque no es bueno. Y no necesitas un puntero en absoluto. – Superlokkus

+0

@ leon22: El manejo del tiempo de espera depende del contexto de su aplicación. Si el proceso está a punto de finalizar, escribo una advertencia en el archivo de registro y lo libero en el olvido. En otros casos, podría matar el hilo. Cuando el hilo principal del proceso finaliza, la mayoría de los sistemas operativos matarán automáticamente a todos los demás hilos dentro del proceso. – ManuelAtWork

Cuestiones relacionadas