【问题标题】:How to pass and run a callback method in Python如何在 Python 中传递和运行回调方法
【发布时间】:2011-07-23 14:22:27
【问题描述】:

我有一个管理器(主线程),它创建其他线程来处理各种操作。 我希望我的经理在它创建的线程结束时得到通知(当 run() 方法执行完成时)。

我知道我可以通过使用 Thread.isActive() 方法检查所有线程的状态来做到这一点,但是轮询很糟糕,所以我想要通知。

我正在考虑给线程一个回调方法,并在 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?
    ...

谢谢!

【问题讨论】:

  • 我对非线程回调方法示例感兴趣。

标签: python multithreading callback notifications


【解决方案1】:

线程不能调用管理器,除非它有对管理器的引用。发生这种情况的最简单方法是让管理器在实例化时将其提供给线程。

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()

如果您希望能够将任意函数或方法分配为回调,而不是存储对管理器对象的引用,那么由于方法包装器等,这会变得有点问题。很难设计回调,因此它会同时获得对管理器线程的引用,这正是您想要的。我为此研究了一段时间,但没有提出任何我认为有用或优雅的东西。

【讨论】:

  • 最后一行不应该是thread.start()吗?否则,不会发生实际的多线程,MyThread.run() 会像任何其他函数一样被执行。但是如果你实际运行MyThread.start() 并创建一个新线程,self.parent.on_thread_finished(self, 42) 仍将在新线程的上下文中执行,而不是在主线程中执行。您将需要某种同步,例如 Queue,所以 @jonathan-lillesæter 实际上是正确的。
  • Python 新手。这不是滥用继承吗? MyThread 看起来不像是 Manager 的逻辑子代。
  • 我觉得值得注意的是,使用这个方案会在辅助线程内部执行回调函数,而不是在主线程内部。另外,在函数 run 中评估 self.parent 有什么用?如果 parentNone ,这是为了防止 self.parent.on_thread_finished 被调用吗?为什么要这样做而不是使用 if 语句?这令人困惑......
【解决方案2】:

这样做有什么问题吗?

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"

【讨论】:

  • 您不能将本地方法 on_thread_finished 作为回调传递给线程,因为本地方法需要两个参数。当从线程调用回调时,它只会给出一个参数(数据)
  • 我知道这已经很晚了,但对于其他将要花一个小时重写大量代码的人来说:Bharathwaaj 的评论是完全不正确,这个答案非常有效。
  • @Shariq,问题是on_thread_finished 仍然在子线程中运行,而不是在Manager 中。你不能让Manager 这样做,因为此时Manager 正在做其他事情。要查看问题,只需在调用MyThread() 后使Test() 陷入死循环。或者,定义一个局部变量并尝试在on_thread_finished() 中使用它,例如number_of_threads_running -=1
  • 如果管理器在 tkinter GUI 中,这将不起作用,您不能从工作线程调用 GUI 的函数
【解决方案3】:

如果您希望主线程等待子线程完成执行,您最好使用某种同步机制。如果只是在一个或多个线程完成执行时收到通知,Condition 就足够了:

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()

但是,使用Queue 可能会更好,因为它可以更轻松地处理多个工作线程。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多