2012-03-30 8 views
18

Kann ein Timeout für einen Anruf auf std::thread::join() festgelegt werden? Ich möchte den Fall behandeln, in dem der Thread zu lange dauert, oder den Thread beenden. Ich mache das vielleicht für mehrere Threads (sagen wir bis zu 30).Timeout für thread.join()

Vorzugsweise ohne Boost, aber ich wäre an einer Boost-Lösung interessiert, wenn das der beste Weg ist.

+0

Was möchten Sie tun, wenn die Zeit abläuft? Würde der Thread beendet werden? –

+4

Dies ist, was "std :: thread :: native_handle" ist - verwenden Sie Betriebssystemfunktionen, um eine zeitgesteuerte Verknüpfung zu erstellen. – ildjarn

Antwort

13

Es gibt keine Zeitüberschreitung für std::thread::join(). Sie können jedoch std::thread::join() nur als Komfortfunktion anzeigen. Mit condition_variable s können Sie eine sehr reichhaltige Kommunikation und Kooperation zwischen Ihren Threads erstellen, einschließlich zeitgesteuerter Wartezeiten. Zum Beispiel:

#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 diesem Beispiel main startet mehrere Threads zu tun, von denen alle gelegentlich überprüfen, ob es an der Zeit unter einem Mutex zu beenden ist (dies kann auch ein Atom sein könnte). Der Haupt-Thread überwacht auch, ob es Zeit ist, zu beenden (wenn die Threads ihre ganze Arbeit erledigen). Wenn main keine Geduld mehr hat, erklärt er einfach, dass es Zeit ist, zu beenden, und wartet dann darauf, dass alle Threads vor dem Beenden alle notwendigen Bereinigungen durchführen.

+1

Das ist nicht ganz richtig, da 'thread_count' atomar sein muss, oder? –

+1

@MooingDuck: Nicht in diesem Fall, weil überall 'thread_count' gelesen oder geschrieben wird, ist im gesperrten Zustand von' m'. So wäre 'thread_count' atomic korrekt, aber redundant und unnötig teuer. –

+0

Was meine Aufmerksamkeit erregt hat, war 'while (thread_count> 0) cv.wait (lk);'. Ist das nicht eine jener Dinger, die 'thread_count' zwischenspeichern dürfen und nicht" wie erwartet "funktionieren? –

3

timed_join() ist jetzt veraltet. Verwenden Sie try_join_for() statt:

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

Ja, es ist möglich. Die Lösung, die von Galik vorgeschlagen wurde, sieht wie folgt aus:

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

} 

Dies ist jedoch im Wesentlichen einen dritten Thread startet, die die thread.join() ausführt.

. (Anmerkung: Der Destruktor von future wird blockiert, bis thread und der Hilfs Thread beendet hat beigetreten)


Vielleicht einen Thread startet gerade ist, einen anderen Thread bringen nicht das, was Sie wollen. Es gibt eine andere, tragbare Lösung ohne Hilfsgewinde:

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

wo T_return der Rückgabetyp der Threads Prozedur ist. In diesem Szenario wird eine std::future/std::async Kombination anstelle einer std::thread verwendet.

Beachten Sie, dass hThread ein Zeiger ist. Wenn Sie den Operator delete aufrufen, wird der Destruktor *hThread aufgerufen und blockiert, bis der Thread beendet ist.

Ich habe beide Versionen mit gcc 4.9.3 auf Cygwin getestet.

+1

Guter Ansatz, aber ich muss auf mehrere Threads warten. (Ich verwende std :: thread threads [nrOfThreads]) Ist die erste Lösung in einer for-Schleife über Threads anwendbar? Und was soll ich tun, wenn die Zeitüberschreitung überschritten wird? Thx – leon22

+0

Die erste Hälfte ist ein nettes Work-Around, aber die zweite Hälfte macht keinen Sinn und es ist schlecht Stil: Warum verwenden Sie nicht einfach eine Zukunft, wenn Sie Ihre Threads Funktionalität in eine Funktion mit zukünftigen Semantik setzen können ? Warum der einfache Zeiger mit neuen und löschen? Offensichtlich sprachen wir über> = C++ 11, also ist diese undichte Vorgehensweise nicht gut. Und Sie brauchen überhaupt keinen Zeiger. – Superlokkus

+0

@ leon22: Die Behandlung des Timeouts hängt vom Kontext Ihrer Anwendung ab. Wenn der Prozess bald beendet wird, schreibe ich eine Warnung in die Logdatei und lasse den Thread in Vergessenheit geraten. In anderen Fällen könnte ich den Thread töten. Wenn der Hauptthread des Prozesses beendet wird, werden die meisten Betriebssysteme alle anderen Threads innerhalb des Prozesses automatisch beenden. – ManuelAtWork