web-dev-qa-db-fra.com

Je veux tuer un std :: thread en utilisant son objet thread?

Duplicata possible:
interruption de thread C++ 0x

J'essaie de tuer/arrêter un thread c ++ std :: en utilisant son objet thread.

Comment peut-on le faire?

26
CPS

La réponse de @ bamboon est bonne, mais je pense que cela mérite une déclaration plus forte.

Quelle que soit la langue que vous utilisez, votre programme va acquérir et libérer des ressources: mémoire, descripteurs de fichiers, ... Pour les programmes simples qui sont lancés en une seule fois, les fuites de ressources n'ont pas beaucoup d'importance: lorsque le programme se termine, les systèmes d'exploitation modernes reprennent automatiquement les ressources ; cependant, pour les programmes de longue durée, une exigence de base est de ne pas fuir les ressources, ou du moins pas de manière répétitive.

Par conséquent, vous devriez avoir appris dès le début que lorsque vous acquérez une ressource, vous devrez vous assurer qu'elle est libérée à un moment donné:

void foo(int i) {
    int* array = malloc(sizeof(int) * i);

    /* do something */

    free(array);
}

Alors posez-vous la question:

  • que se passe-t-il lorsque je tue le programme?
  • que se passe-t-il lorsque je tue le fil?

Eh bien, comme nous l'avons dit, lorsqu'un programme se termine, le système d'exploitation rassemble les ressources, donc en supposant (et c'est une hypothèse) que vous n'avez pas acquis de ressource sur une autre système OR que ce système est bien protégé contre de tels abus, aucun mal, aucune faute.

Cependant, lorsque vous tuez un thread, le programme s'exécute toujours, donc le système d'exploitation ne recueille pas les ressources. Vous avez perdu de la mémoire, vous avez verrouillé un fichier pour l'écriture que vous ne pouvez plus déverrouiller, ... Vous ne devez pas tuer les threads.

Les langages de niveau supérieur ont un moyen de gérer cela: les exceptions. Étant donné que les programmes doivent être protégés contre les exceptions de toute façon, Java (par exemple) va tuer un thread en le mettant en pause, en lançant une exception au moment de l'exécution et en déroulant doucement la pile. Cependant, il n'y en a pas encore en C++.

Est-ce impossible? Non, évidemment pas. En fait, vous pourriez parfaitement réutiliser la même idée:

  • encapsuler std::thread, interruptible_thread la classe contiendra également un indicateur d'interruption
  • transmettez l'adresse du drapeau à std::thread lors du lancement, et stockez-le de façon locale
  • instrumentez votre code avec des points de contrôle où vous vérifiez si l'indicateur d'interruption est défini ou non, et quand il déclenche une exception

C'est:

// Synopsis
class interrupt_thread_exception;
class interruptible_thread;
void check_for_interrupt();

// Interrupt exception
class interrupt_thread_exception: public virtual std::exception {
public:
    virtual char const* what() const override { return "interrupt"; }
}; // class interrupt_thread_exception

// Interruptible thread
class interruptible_thread {
public:
    friend void check_for_interrupt();

    template <typename Function, typename... Args>
    interruptible_thread(Function&& fun, Args&&... args):
        _thread([](std::atomic_bool& f, Function&& fun, Args&&... args) {
                    _flag_ref = &f; fun(std::forward<Args>(args)...);
                },
                _flag,
                std::forward<Function>(fun),
                std::forward<Args>(args)...)
    {}

    bool stopping() const { return _flag.load(); }

    void stop() { _flag.store(true); }

private:
    static thread_local std::atomic_bool* _flag_ref = nullptr;

    std::atomic_bool _flag = false;
    std::thread _thread;
}; // class interruptible_thread

// Interruption checker
inline void check_for_interrupt() noexcept(false) {
    if (not interruptible_thread::_flag_ref) { return; }
    if (not interruptible_thread::_flag_ref->load()) { return; }

    throw interrupt_thread_exception();
} // check_for_interrupt

Maintenant, vous pouvez simplement saupoudrer votre code threadé avec des contrôles d'interruption aux endroits appropriés.

39
Matthieu M.

Tu ne peux pas.

std::threads ne sont pas interruptibles. Vous pouvez utiliser boost::thread qui offre cette fonctionnalité.

Boost le fait en définissant des "points d'interruption" sur lesquels le thread se terminera s'il est interrompu et atteint un tel point.

Néanmoins, la plupart du temps, penser à une refonte pourrait être le moyen le plus propre et le plus simple d'atteindre ce que vous essayez d'atteindre.

Si vous êtes toujours à la recherche d'une implémentation C++ 11 de threads interruptibles, consultez le livre d'Anthony Williams (propriétaire du boost boost) "C++ Concurrency in Action". Il passe par une mise en œuvre de base de la façon dont une telle chose peut être réalisée.

std::thread::native_handle vous donne accès au gestionnaire de threads sous-jacent spécifique à la plate-forme qui pourrait prendre en charge l'interruption, mais cette approche rend votre code non transférable et probablement pas plus propre en aucune façon.

27
inf