Come verificare se uno std :: thread è ancora in esecuzione?


86

Come posso verificare se un std::threadè ancora in esecuzione (in modo indipendente dalla piattaforma)? Manca un timed_join()metodo e joinable()non è pensato per questo.

Ho pensato di bloccare un mutex con a std::lock_guardnel thread e di utilizzare il try_lock()metodo del mutex per determinare se è ancora bloccato (il thread è in esecuzione), ma mi sembra inutilmente complesso.

Conosci un metodo più elegante?

Aggiornamento: Per essere chiari: voglio controllare se il thread è uscito in modo pulito o meno. Un thread "sospeso" è considerato in esecuzione per questo scopo.


Immagino che controllare se un thread è ancora in esecuzione è importante solo quando te lo aspetti wait()e, in tal caso, se non l'hai wait()ancora fatto, deve essere in esecuzione per definizione. Ma questo ragionamento potrebbe essere inesatto.
ere Il

In realtà ho un thread che esce in condizioni eccezionali e voglio controllare dal thread principale se è ancora in esecuzione, ma non voglio aspettare (partecipare)
kispaljr

1
Cosa intendi esattamente per corsa? Vuoi dire che sta elaborando attivamente piuttosto che in uno stato di attesa, o vuoi dire che il thread esiste ancora e non è terminato?
CashCow

Puoi sempre usare boost :)
CashCow

4
Non avresti dovuto accettare una risposta se non eri soddisfatto.
Nicol Bolas

Risposte:


120

Se sei disposto a utilizzare C ++ 11 std::asynce std::futureper eseguire le tue attività, puoi utilizzare la wait_forfunzione distd::future per verificare se il thread è ancora in esecuzione in un modo ordinato come questo:

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

int main() {
    using namespace std::chrono_literals;

    /* Run some task on new thread. The launch policy std::launch::async
       makes sure that the task is run asynchronously on a new thread. */
    auto future = std::async(std::launch::async, [] {
        std::this_thread::sleep_for(3s);
        return 8;
    });

    // Use wait_for() with zero milliseconds to check thread status.
    auto status = future.wait_for(0ms);

    // Print status.
    if (status == std::future_status::ready) {
        std::cout << "Thread finished" << std::endl;
    } else {
        std::cout << "Thread still running" << std::endl;
    }

    auto result = future.get(); // Get result.
}

Se devi usare std::thread, puoi usare std::promiseper ottenere un oggetto futuro:

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

int main() {
    using namespace std::chrono_literals;

    // Create a promise and get its future.
    std::promise<bool> p;
    auto future = p.get_future();

    // Run some task on a new thread.
    std::thread t([&p] {
        std::this_thread::sleep_for(3s);
        p.set_value(true); // Is done atomically.
    });

    // Get thread status using wait_for as before.
    auto status = future.wait_for(0ms);

    // Print status.
    if (status == std::future_status::ready) {
        std::cout << "Thread finished" << std::endl;
    } else {
        std::cout << "Thread still running" << std::endl;
    }

    t.join(); // Join thread.
}

Entrambi questi esempi produrranno:

Thread still running

Questo è ovviamente perché lo stato del thread viene verificato prima che l'attività sia terminata.

Ma poi di nuovo, potrebbe essere più semplice farlo come altri hanno già menzionato:

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

int main() {
    using namespace std::chrono_literals;

    std::atomic<bool> done(false); // Use an atomic flag.

    /* Run some task on a new thread.
       Make sure to set the done flag to true when finished. */
    std::thread t([&done] {
        std::this_thread::sleep_for(3s);
        done = true;
    });

    // Print status.
    if (done) {
        std::cout << "Thread finished" << std::endl;
    } else {
        std::cout << "Thread still running" << std::endl;
    }

    t.join(); // Join thread.
}

Modificare:

C'è anche l' std::packaged_taskuso con std::threadper una soluzione più pulita rispetto all'utilizzo di std::promise:

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

int main() {
    using namespace std::chrono_literals;

    // Create a packaged_task using some task and get its future.
    std::packaged_task<void()> task([] {
        std::this_thread::sleep_for(3s);
    });
    auto future = task.get_future();

    // Run task on new thread.
    std::thread t(std::move(task));

    // Get thread status using wait_for as before.
    auto status = future.wait_for(0ms);

    // Print status.
    if (status == std::future_status::ready) {
        // ...
    }

    t.join(); // Join thread.
}

2
Bella risposta. Vorrei aggiungere che funziona anche con thread senza alcun valore di ritorno e futuro <void>
kispaljr

Qual è il motivo di questo codice std::atomic<bool> done(false);? Non è l' boolatomico di default?
Hi-Angel

6
@YagamyLight In C ++ nulla è atomico per impostazione predefinita a meno che non sia racchiuso in un file std::atomic. sizeof(bool)è definita dall'implementazione e può essere> 1, quindi è possibile che si verifichi una scrittura parziale. C'è anche la questione della coerenza della cache ..
Snps


1
nota che std :: chrono_literals richiederà C ++ 14 per la compilazione
Patrizio Bertoni

5

Una soluzione semplice è avere una variabile booleana che il thread imposta a true a intervalli regolari e che viene controllata e impostata su false dal thread che desidera conoscere lo stato. Se la variabile è falsa per troppo tempo, il thread non è più considerato attivo.

Un modo più thread-safe è avere un contatore che viene aumentato dal thread figlio e il thread principale confronta il contatore con un valore memorizzato e se lo stesso dopo un tempo troppo lungo, il thread figlio viene considerato non attivo.

Nota tuttavia, non c'è modo in C ++ 11 per uccidere o rimuovere effettivamente un thread che è stato bloccato.

Modifica Come verificare se un thread è uscito in modo pulito o meno: Fondamentalmente la stessa tecnica descritta nel primo paragrafo; Avere una variabile booleana inizializzata su false. L'ultima cosa che fa il thread figlio è impostarlo su true. Il thread principale può quindi controllare quella variabile e, se vero, fare un join sul thread figlio senza molto (se presente) blocco.

Edit2 Se il thread esce a causa di un'eccezione, hanno due funzioni "main" del thread: La prima ha un try- catchall'interno della quale chiama la seconda funzione del thread principale "reale". Questa prima funzione principale imposta la variabile "have_exited". Qualcosa come questo:

bool thread_done = false;

void *thread_function(void *arg)
{
    void *res = nullptr;

    try
    {
        res = real_thread_function(arg);
    }
    catch (...)
    {
    }

    thread_done = true;

    return res;
}

1
Se questa è la definizione dell'OP di "esecuzione".
CashCow

Forse mi hai frainteso. Voglio controllare se un thread è uscito in modo pulito o meno. Ci scusiamo per la formulazione poco chiara.
kispaljr

7
Se diversi thread stanno leggendo e scrivendo thread_done, questo codice viene interrotto senza una barriera di memoria. Usa std::atomic<bool>invece.
ildjarn

1
Non mi riferivo a più thread di lavoro, mi riferivo a un singolo thread di lavoro che scriveva boolmentre il thread principale legge da esso - questo richiede una barriera di memoria.
ildjarn

3
Dai un'occhiata a questa domanda per una discussione sul perché a std::atomic<bool>è necessario qui.
Robert Rüger,

2

Questo semplice meccanismo è possibile utilizzare per rilevare la finitura di un thread senza bloccare il metodo di unione.

std::thread thread([&thread]() {
    sleep(3);
    thread.detach();
});

while(thread.joinable())
    sleep(1);

2
staccare il thread alla fine non è ciò che si vuole, e se non lo fai, devi chiamare join()da un thread che non aspetta che il thread perda la sua joinable()proprietà, altrimenti verrà eseguito in loop all'infinito (cioè joinable()restituisce true finché il thread non è effettivamente join()ed e non fino alla sua fine)
Niklas R

joinable significa che un thread sta tenendo un handle di thread. se il thread è fatto, sarà ancora unibile. se hai bisogno di controllare senza aspettare la fine del thread, ecco la soluzione. Sono solo poche righe di codice. Perché non l'hai provato prima?
Evgeny Karpov

L'ho fatto, e il punto che sto cercando di sottolineare è che se rimuovi la thread.detach()parte, il programma sopra non terminerà mai.
Niklas R

sì, non lo farà. è per questo che alla fine chiama distacco.
Evgeny Karpov

1
Questo metodo di chiamata detach elimina la necessità di mutex e altre soluzioni più complesse. Lo uso e funziona! Grazie per la risposta.
settembre

1

Creare un mutex a cui hanno accesso sia il thread in esecuzione che il thread chiamante. Quando il thread in esecuzione si avvia, blocca il mutex e quando finisce sblocca il mutex. Per verificare se il thread è ancora in esecuzione, il thread chiamante chiama mutex.try_lock (). Il valore restituito è lo stato del thread. (Assicurati solo di sbloccare il mutex se try_lock ha funzionato)

Un piccolo problema con questo, mutex.try_lock () restituirà false tra il momento in cui il thread viene creato e quando blocca il mutex, ma questo può essere evitato utilizzando un metodo leggermente più complesso.


-1 Non dovresti usare std::mutexper questo tipo di segnalazione (principalmente a causa di ragioni per cui il mutex è normalmente implementato). Un atomic_flagfunziona altrettanto bene in questo caso con un overhead molto inferiore. A std::futurepotrebbe essere anche migliore in quanto esprime l'intento in modo più chiaro. Inoltre, ricorda che try_lockpotrebbe fallire in modo spurio, quindi il ritorno non è necessariamente lo stato del thread (anche se probabilmente non ti farà molto male in questo caso particolare).
ComicSansMS

1

Puoi sempre controllare se l'id del thread è diverso da std :: thread :: id () costruito di default. Un thread in esecuzione ha sempre un ID associato autentico. Cerca di evitare troppe cose fantasiose :)


0

Sicuramente avere una variabile avvolta da mutex inizializzata a false, che il thread imposta truecome l'ultima cosa che fa prima di uscire. È abbastanza atomico per le tue esigenze?


1
Se usi comunque un mutex, ritengo che la mia soluzione (usando solo il mutex, senza il booleano) sia più elegante. Se si desidera assolutamente utilizzare un booleano thread-safe, consiglierei invece std :: atomic <bool>. Nella maggior parte delle implementazioni sarà privo di blocchi.
kispaljr

Perché chiudere a chiave? Un thread legge sempre e solo, uno scrive sempre e solo. E in ogni caso le scritture a dimensione di parola sono atomiche IIRC.
Xeo

1
@Xeo: la scrittura potrebbe essere atomica, ma è comunque necessaria una barriera di memoria se ti aspetti di vedere il valore scritto su un thread diverso (che potrebbe essere in esecuzione su una CPU diversa). std::atomic<bool>si prende cura di questo per te, motivo per cui è la vera risposta IMO.
ildjarn
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.