2012-03-30 29 views
18

È possibile impostare un timeout per una chiamata a std::thread::join()? Voglio gestire il caso in cui il thread sta impiegando troppo tempo per essere eseguito, o terminare il thread. Potrei farlo per più thread (per esempio, fino a 30).Timeout per thread.join()

Preferibilmente senza boost, ma sarei interessato a una soluzione di boost se questo è il modo migliore.

+0

Cosa vorresti che accadesse quando scade il tempo? Il thread sarebbe terminato? –

+4

Questo è ciò che 'std :: thread :: native_handle' è per usare le strutture del sistema operativo per fare un join temporizzato. – ildjarn

risposta

13

Nessun timeout per std::thread::join(). Tuttavia, è possibile visualizzare std::thread::join() come funzione di semplice utilizzo. Usando condition_variable s puoi creare comunicazioni e cooperazioni molto ricche tra i tuoi thread, incluse le attese temporizzate. Ad esempio:

#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"; 
} 

In questo esempio main lancia diversi thread turistiche lavoro, tutte occasionalmente controllano se è il momento di smettere sotto un mutex (potrebbe anche essere un atomico). Il thread principale controlla anche se è ora di uscire (se i thread ottengono tutto il loro lavoro). Se il principale esaurisce la pazienza, dichiara solo che è ora di uscire, quindi attende che tutti i thread eseguano le necessarie operazioni di pulizia prima di uscire.

+1

Questo non è giusto, dato che 'thread_count' deve essere atomico, non è vero? –

+1

@MooingDuck: non in questo caso perché ovunque 'thread_count' è letto o scritto è sotto lo stato bloccato di' m'. Quindi rendere atomico 'thread_count' sarebbe corretto, ma ridondante e inutilmente costoso. –

+0

In realtà, ciò che attirò la mia attenzione era 'while (thread_count> 0) cv.wait (lk);'. Non è una di quelle cose a cui è permesso mettere in cache 'thread_count' e non lavorare" come ti aspetteresti "? –

3

timed_join() è ora obsoleto. Utilizzare try_join_for() invece:

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

Sì, è possibile. La soluzione che è stata suggerita da Galik si presenta così:

#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. --- */ 

} 

Tuttavia, questo lancia essenzialmente un terzo filo che esegue la thread.join().

(Nota:. Il distruttore di future bloccherà fino thread è unito e il filo ausiliario ha terminato)


Forse lanciare un filo solo di portare un altro filo verso il basso non è ciò che si desidera. C'è un'altra soluzione portatile senza un filo ausiliario:

#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; 

dove T_return è il tipo di ritorno della routine thread. Questo scenario utilizza una combinazione std::future/std::async anziché std::thread.

Nota che hThread è un puntatore. Quando si chiama l'operatore delete, verrà richiamato il distruttore di *hThread e il blocco fino al termine del thread.

Ho testato entrambe le versioni con gcc 4.9.3 su Cygwin.

+1

Buon approccio, ma devo aspettare diversi thread. (Io uso std :: thread threads [nrOfThreads]) La prima soluzione è applicabile in un ciclo for su thread? E cosa dovrei fare se il timeout dovesse superare? Thx – leon22

+0

Il primo semestre è un bel work-around, ma la seconda metà non ha senso più è brutto stile: perché non usi un futuro solo se riesci a mettere la funzionalità dei thread in una funzione con la semantica futura ? Perché il semplice puntatore con nuovo e cancella? Ovviamente stavamo parlando di> = C++ 11, quindi questo approccio leaky non va bene. E non hai affatto bisogno di un puntatore. – Superlokkus

+0

@ leon22: la gestione del timeout dipende dal contesto dell'applicazione. Se il processo sta per terminare, scrivo un avviso nel file di log e rilascio il thread nel dimenticatoio. In altri casi potrei uccidere il thread. Quando termina il thread principale del processo, la maggior parte dei sistemi operativi ucciderà automaticamente tutti gli altri thread all'interno del processo. – ManuelAtWork