web-dev-qa-db-fra.com

Comment passer et exécuter une méthode de rappel dans Python

J'ai un gestionnaire (thread principal), qui crée d'autres threads pour gérer diverses opérations. Je voudrais que mon gestionnaire soit averti lorsqu'un thread qu'il a créé se termine (lorsque l'exécution de la méthode run () est terminée).

Je sais que je pourrais le faire en vérifiant l'état de tous mes threads avec la méthode Thread.isActive (), mais l'interrogation craint, donc je voulais avoir des notifications.

Je pensais donner une méthode de rappel aux Threads, et appeler cette fonction à la fin de la méthode run ():

class Manager():
    ...
    MyThread(self.on_thread_finished).start() # How do I pass the callback

    def on_thread_finished(self, data):
        pass
    ...

class MyThread(Thread):
    ...
    def run(self):
        ....
        self.callback(data) # How do I call the callback?
    ...

Merci!

26
nbarraille

Le thread ne peut pas appeler le gestionnaire à moins qu'il ait une référence au gestionnaire. Le moyen le plus simple pour que cela se produise est que le gestionnaire le donne au thread lors de l'instanciation.

class Manager(object):
    def new_thread(self):
        return MyThread(parent=self)
    def on_thread_finished(self, thread, data):
        print thread, data

class MyThread(Thread):

    def __init__(self, parent=None):
        self.parent = parent
        super(MyThread, self).__init__()

    def run(self):
        # ...
        self.parent and self.parent.on_thread_finished(self, 42)

mgr    = Manager()
thread = mgr.new_thread()
thread.start()

Si vous souhaitez pouvoir affecter une fonction ou une méthode arbitraire en tant que rappel, plutôt que de stocker une référence à l'objet gestionnaire, cela devient un peu problématique en raison des wrappers de méthode et autres. Il est difficile de concevoir le rappel, il obtient donc une référence à la fois au gestionnaire et au thread, ce que vous voudrez. J'ai travaillé là-dessus pendant un certain temps et je n'ai rien trouvé de utile ou d'élégant.

21
kindall

Quelque chose de mal à le faire de cette façon?

from threading import Thread

class Manager():
    def Test(self):
        MyThread(self.on_thread_finished).start()

    def on_thread_finished(self, data):
        print "on_thread_finished:", data

class MyThread(Thread):
    def __init__(self, callback):
        Thread.__init__(self)
        self.callback = callback

    def run(self):
        data = "hello"
        self.callback(data)

m = Manager()
m.Test() # prints "on_thread_finished: hello"
9
combatdave

Si vous voulez que le thread principal attende que les threads enfants terminent l'exécution, il est probablement préférable d'utiliser une sorte de mécanisme de synchronisation. Si simplement être averti quand un ou plusieurs threads ont fini de s'exécuter, une Condition suffit:

import threading

class MyThread(threading.Thread):
    def __init__(self, condition):
        threading.Thread.__init__(self)
        self.condition = condition

    def run(self):
        print "%s done" % threading.current_thread()
        with self.condition:
            self.condition.notify()


condition = threading.Condition()
condition.acquire()

thread = MyThread(condition)
thread.start()

condition.wait()

Cependant, l'utilisation de Queue est probablement meilleure, car elle facilite un peu la gestion de plusieurs threads de travail.

3