简体   繁体   English

如何与工作线程通信

[英]How to communicate with worker thread

I'm using a library which heaviliy uses I/O. 我正在使用大量使用I / O的库。 For that reason calls to that library can last very long (more than 5 seconds) possible. 因此,对该库的调用可能会持续很长时间(超过5秒)。 Using that directly inside an UI is not a good idea because it will freeze. UI中直接使用它不是一个好主意,因为它将冻结。

For that reason I outsourced the library calls to a thread queue like shown in this example: Python threads: communication and stopping 因此,我将库调用外包到线程队列,如本例所示: Python线程:通信和停止

Nevertheless I'm not very happy with that solution since this has a major drawback: 不过,我对此解决方案并不满意,因为这有一个主要缺点:

  • I cannot really communicate with the UI. 我真的无法与用户界面进行交流。

Every lib command returns a return message, which can either be an error message or some computational result. 每个lib命令都返回一条返回消息,该返回消息可以是错误消息或某些计算结果。 How would I get this? 我怎么得到这个?

Consider a library call do_test(foo) : 考虑一个库调用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?

Can someone give me advice how to realize such a pattern? 有人可以给我建议如何实现这种模式吗?

Edit: This here is a minimal example: 编辑:这是一个最小的示例:

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)

I personally use PySide but I don't want to depend this library on PySide or any other ui-related library. 我个人使用PySide但我不想将此库依赖于PySide或任何其他与ui相关的库。

I thought a bit about my implementations. 我对自己的实现做了一些思考。 THe conclusion is that I start another thread for picking the results of the queue: 结论是,我启动了另一个线程来选择队列结果:

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

The given callback from an UI or elsewhere is called with every result from the queue. 来自UI或其他位置的给定回调将与队列中的每个结果一起调用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM