web-dev-qa-db-fra.com

Fermer toutes les discussions avec une interruption du clavier

Ce que j'essaie de faire ici est d'utiliser une interruption de clavier pour quitter tous les threads en cours du programme. Ceci est une version réduite de mon code où le thread est créé:

for i in taskDictionary:
    try:
        sleep(60)
        thread = Thread(target = mainModule.executeThread)
        thread.start()
    except KeyboardInterrupt:
        thread.__stop()

Le programme lui-même est beaucoup plus compliqué, prenant en compte des tonnes de variables différentes qui affectent les threads et ayant même la possibilité de se lancer en mode séquentiel, où les tâches ne sont pas threadées, mais lancées une par une, ce qui peut poser problème. Avec cette petite variation, je viens d’évoquer… .. Je l’ai fait de manière à obtenir des résultats de 50/50. Les interruptions fonctionneraient mais les threads ne sortiraient jamais proprement. Parfois, ils continuaient mais arrêtaient l'exécution des futurs threads, parfois ils sortaient avec une erreur massive concernant l'interruption, d'autres fois, les interruptions ne faisaient rien du tout. La dernière fois que j'ai exécuté ce programme, le programme a arrêté l'exécution de tous les futurs threads, mais pas le thread actuel . Y a-t-il un moyen de quitter les threads sans accéder au module dans lequel le thread est en train de s'exécuter?

34
Joe

Une question similaire est "Comment tuez-vous un fil?"

Vous créez dans votre thread un gestionnaire de sortie contrôlé par un objet verrou ou événement du module de thread. Vous supprimez alors simplement le verrou ou signalez l’objet événement. Ceci informe le thread qu'il doit arrêter le traitement et sortir normalement. Après avoir signalé le thread dans votre programme principal, il ne reste plus qu’à utiliser la méthode thread.join() dans main qui attend que le thread s’arrête.

Un petit exemple:

import threading
import time

def timed_output(name, delay, run_event):
    while run_event.is_set():
        time.sleep(delay)
        print name,": New Message!"


def main():
    run_event = threading.Event()
    run_event.set()
    d1 = 1
    t1 = threading.Thread(target = timed_output, args = ("bob",d1,run_event))

    d2 = 2
    t2 = threading.Thread(target = timed_output, args = ("paul",d2,run_event))

    t1.start()
    time.sleep(.5)
    t2.start()

    try:
        while 1:
            time.sleep(.1)
    except KeyboardInterrupt:
        print "attempting to close threads. Max wait =",max(d1,d2)
        run_event.clear()
        t1.join()
        t2.join()
        print "threads successfully closed"

if __== '__main__':
    main()

Si vous avez VRAIMENT besoin des fonctionnalités de tuer un thread, utilisez le multitraitement. Il vous permet d'envoyer des SIGTERM à des "processus" individuels (il est également très similaire au module de threading). En règle générale, les threads sont valables lorsque vous êtes lié aux entrées-sorties et le multitraitement est destiné aux personnes réellement liées au processeur.

54
Paul Seeb

Quelques options ne nécessitent pas l'utilisation de verrous ou d'autres signaux entre les threads. L'une consiste à définir les threads en tant que daemons , qui seront automatiquement supprimés à la sortie du thread principal. L'autre utilise process à la place, qui a une méthode de terminaison que vous pouvez appeler à partir du processus principal, si vous aviez besoin de les tuer et de maintenir le programme principal en cours d'exécution.

Les deux sont particulièrement utiles si vous avez des threads bloquant en entrée. Bien que l'utilisation d'un délai d'attente et la vérification périodique du signal fonctionnent dans la plupart des cas sans trop de charge, l'utilisation de démons ou de processus élimine le besoin de boucles occupées ou de complexité supplémentaire importante.

Voir les réponses à cette question pour plus de détails sur ces solutions et une discussion sur le problème de la suppression des fils.

0
Scott Yeager