【问题标题】:How to communicate with worker thread如何与工作线程通信
【发布时间】:2013-07-21 09:20:36
【问题描述】:

我正在使用大量使用 I/O 的库。因此,对该库的调用可能会持续很长时间(超过 5 秒)。 直接在 UI 中使用它不是一个好主意,因为它会冻结。

出于这个原因,我将库调用外包给了一个线程队列,如下例所示:Python threads: communication and stopping

不过我对这个解决方案不是很满意,因为它有一个主要缺点:

  • 我无法真正与 UI 交流。

每个 lib 命令都会返回一个返回消息,它可以是错误消息,也可以是一些计算结果。 我怎么会得到这个?

考虑调用库do_test(foo)

def do_test(foo):
    time.sleep(10)
    return random.random() * foo

def ui_btn_click():
    threaded_queue.put((do_test, 42))
    # Now how to display the result without freezing the UI?

有人可以给我建议如何实现这种模式吗?

编辑: 这是一个最小的例子:

import os, time, random
import threading, queue

CMD_FOO = 1
CMD_BAR = 2

class ThreadedQueue(threading.Thread):
    def __init__(self):
        super().__init__()
        self.in_queue = queue.Queue()
        self.out_queue = queue.Queue()
        self.__stoprequest = threading.Event()

    def run(self):
        while not self.__stoprequest.isSet():
            (cmd, arg) = self.in_queue.get(True)

            if cmd == CMD_FOO:
                ret = self.handle_foo(arg)
            elif cmd == CMD_BAR:
                ret = self.handle_bar(arg)
            else:
                print("Unsupported cmd {0}".format(cmd))
            self.out_queue.put(ret)
            self.in_queue.task_done()

    def handle_foo(self, arg):
        print("start handle foo")
        time.sleep(10)
        return  random.random() * arg

    def handle_bar(self, arg):
        print("start handle bar")
        time.sleep(2)
        return (random.random() * arg, 2 * arg)


if __name__ == "__main__":
    print("START")
    t = ThreadedQueue()
    t.start()
    t.in_queue.put((CMD_FOO, 10))
    t.in_queue.put((CMD_BAR, 10))

    print("Waiting")

    while True:
        x = t.out_queue.get(True)
        t.out_queue.task_done()
        print(x)

我个人使用 PySide,但我不想将此库依赖于 PySide 或任何其他与 ui 相关的库。

【问题讨论】:

  • 如果没有看到更多代码,很难变得更加具体,但总体思路是,您有一个“工作”线程从一个队列中消费数据并将结果放入另一个队列中,以便使用它由另一个线程。你的 UI 用的是什么?您是否有一个运行应用程序主“循环”的 ui 线程?
  • 我已经更新了一个最小的例子。我需要从第三个线程访问out_queue 吗?
  • 我不确定你的问题。是不是不想让主线程等待结果?
  • 基本上是的。我希望附加的用户界面在进行长时间的通话时感觉响应。出于这个原因,我已经完成了命令队列。但是如何更新响应?
  • 我认为您的代码几乎就在那里-在'main'循环中不要使用 out queue.get(True) - True 告诉线程阻塞,直到一个项目在队列中。在 try...except 块中传递 False 或调用 get_nowait() 并捕获 Empty 异常。这将使您的主循环不会阻塞。

标签: python multithreading concurrency threadpool


【解决方案1】:

我对我的实现进行了一些思考。结论是我启动另一个线程来挑选队列的结果:

class ReceiveThread(threading.Thread):
    """
    Processes the output queue and calls a callback for each message
    """
    def __init__(self, queue, callback):
        super().__init__()
        self.__queue = queue
        self.__callback = callback
        self.__stoprequest = threading.Event()
        self.start()

    def run(self):
        while not self.__stoprequest.isSet():
            ret = self.__queue.get(True)
            self.__callback(ret)
            self.__queue.task_done()

队列中的每个结果都会调用来自 UI 或其他地方的给定回调。

【讨论】:

    猜你喜欢
    • 2013-10-09
    • 1970-01-01
    • 2018-08-29
    • 1970-01-01
    • 1970-01-01
    • 2023-03-16
    • 1970-01-01
    • 1970-01-01
    • 2023-04-08
    相关资源
    最近更新 更多