100 votes

Comment vérifier si un std::thread est toujours en cours d'exécution ?

Comment puis-je vérifier si un std::thread fonctionne toujours (de manière indépendante de la plateforme) ? Il manque un timed_join() et joinable() n'est pas fait pour ça.

J'ai pensé à verrouiller un mutex avec un std::lock_guard dans le fil de discussion et en utilisant le try_lock() du mutex pour déterminer s'il est toujours verrouillé (le thread est en cours d'exécution), mais cela me semble inutilement complexe.

Connaissez-vous une méthode plus élégante ?

Mise à jour : Pour être clair : je veux vérifier si le thread est sorti proprement ou non. Un fil "suspendu" est considéré comme étant en cours d'exécution dans ce but.

136voto

Snps Points 4082

Si vous êtes prêt à utiliser C++11 std::async et std::future pour l'exécution de vos tâches, alors vous pouvez utiliser la fonction wait_for fonction de std::future pour vérifier si le fil est toujours en cours d'exécution d'une manière soignée comme ceci :

#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.
}

Si vous devez utiliser std::thread alors vous pouvez utiliser std::promise pour obtenir un objet futur :

#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.
}

Ces deux exemples donnent un résultat :

Thread still running

Ceci est bien sûr dû au fait que le statut du fil est vérifié avant que la tâche ne soit terminée.

Mais là encore, il pourrait être plus simple de faire comme les autres l'ont déjà mentionné :

#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.
}

Edit :

Il y a aussi le std::packaged_task à utiliser avec std::thread pour une solution plus propre que l'utilisation de 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.
}

5voto

Joachim Pileborg Points 121221

Une solution facile est d'avoir une variable booléenne que le thread met à vrai à intervalles réguliers, et qui est vérifiée et mise à faux par le thread qui veut connaître le statut. Si la variable est fausse pendant trop longtemps, le fil n'est plus considéré comme actif.

Une façon plus sûre pour les threads est d'avoir un compteur qui est augmenté par le thread enfant, et le thread principal compare le compteur à une valeur stockée et si c'est la même chose après un temps trop long, alors le thread enfant est considéré comme non actif.

Notez cependant qu'il n'existe aucun moyen en C++11 de tuer ou de supprimer un thread qui s'est suspendu.

Modifier Comment vérifier si un thread a quitté proprement ou non : En gros, la même technique que celle décrite dans le premier paragraphe ; Avoir une variable booléenne initialisée à false. La dernière chose que fait le thread enfant est de la mettre à true. Le thread principal peut alors vérifier cette variable, et si elle est vraie, faire une jointure sur le thread enfant sans beaucoup (ou pas du tout) de blocage.

Edit2 Si le thread se termine à cause d'une exception, alors il y a deux fonctions "main" du thread : La première a un try - catch à l'intérieur duquel il appelle la deuxième "vraie" fonction du fil principal. Cette première fonction principale définit la variable "have_exited". Quelque chose comme ça :

bool thread_done = false;

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

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

    thread_done = true;

    return res;
}

1voto

Nathan Fuchs Points 36

Créez un mutex auquel le thread en cours d'exécution et le thread appelant ont tous deux accès. Lorsque le thread en cours d'exécution démarre, il verrouille le mutex, et lorsqu'il se termine, il le déverrouille. Pour vérifier si le thread est toujours en cours d'exécution, le thread appelant appelle mutex.try_lock(). La valeur de retour de cet appel est le statut du thread. (Assurez-vous de déverrouiller le mutex si le try_lock a fonctionné).

Un petit problème se pose : mutex.try_lock() renvoie false entre le moment où le thread est créé et celui où il verrouille le mutex, mais cela peut être évité en utilisant une méthode légèrement plus complexe.

0voto

CashCow Points 18388

Je ne suis pas sûr pour std::thread mais boost::thread a au moins une fonction timed_join() donc si vous lui passez une durée de 0, elle renverra vrai si le thread s'est terminé et faux s'il a expiré.

Donc, si elle renvoie false, votre thread est toujours en cours d'exécution.

De quelle manière exactement joinable() "pas fait pour ça" ? Comme je pense que ma méthode peut simplement faire la même chose que joinable() de toute façon (sauf à retourner le contraire).

0voto

Il y a bien sûr une variable enveloppée par un mutex initialisée à false que le thread définit comme true comme la dernière chose qu'il fait avant de quitter. Est-ce assez atomique pour vos besoins ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X