Python线程,事件和队列如何协同工作?

时间:2018-08-29 10:44:12

标签: python multithreading concurrency queue python-multithreading

看了比斯利书中的例子之后,我正在和我的朋友聊天

class ActorExit(Exception):
    pass

class Actor:
    def __init__(self):
        self._mailbox = Queue()

    def send(self, msg):
        self._mailbox.put(msg)

    def recv(self):
        msg = self._mailbox.get()
        if msg is ActorExit:
            raise ActorExit()
        return msg

    def close(self):
        self.send(ActorExit)

    def start(self):
        self._terminated = Event()
        t = Thread(target=self._bootstrap)
        t.daemon = True
        t.start()

    def _bootstrap(self):
        try:
            self.run()
        except ActorExit:
            pass
        finally:
            self._terminated.set()

    def join(self):
        self._terminated.wait()

    def run(self):
        while True:
            msg = self.recv()

class PrintActor(Actor):
    def run(self):
        while True:
            msg = self.recv()
            print('Got:', msg)

我的朋友认为Event的唯一目的是阻塞主线程,直到另一个线程执行set操作为止。 真的吗? 我们如何看待线程执行?

1 个答案:

答案 0 :(得分:2)

  

Python线程,事件和队列如何一起工作?

他们没有。您可以使用无队列的事件和无事件的队列,彼此之间没有依赖关系。您的示例恰好同时使用了两者。

  

我的朋友认为Event的唯一目的是阻塞主线程,直到另一个线程执行set操作为止。是真的吗?

在事件对象上调用.wait()将阻塞 any 调用线程,直到内部标志为.set()

如果查看事件的来源,您会发现事件仅由一个带锁的条件变量和一个布尔标志+用于处理和传达(与等待的线程)该标志状态变化的方法组成。

>
class Event:

    """Class implementing event objects.
    Events manage a flag that can be set to true with the set() method and reset
    to false with the clear() method. The wait() method blocks until the flag is
    true.  The flag is initially false.
    """

    def __init__(self):
        self._cond = Condition(Lock())
        self._flag = False
    ...
  

我们如何看待线程执行情况?

一种简单的方法是应用某种实用工具功能,以打印出您感兴趣的内容,例如:

def print_info(info=""):
    """Print calling function's name and thread with optional info-text."""
    calling_func = sys._getframe(1).f_code.co_name
    thread_name = threading.current_thread().getName()
    print(f"<{thread_name}, {calling_func}> {info}", flush=True)

另一种可能性是像在answer中那样使用日志记录。


不确定Beazly想要用您显示的代码来演示什么,但是对于这个简单的任务,我认为它有点过分设计。当您已经使用队列时,不需要在此处涉及事件。您可以通过传递哨兵值来初始化线程终止。

以下是带有哨兵('STOP')的示例的简化版本,以及上面带有print_info的一些信息打印件:

import sys
import time
import threading
from queue import Queue


class Actor(threading.Thread):

    def __init__(self):
        super().__init__(target=self.run)
        self.queue = Queue()

    def send(self, msg):
        self.queue.put(msg)
        print_info(f"sent: {msg}")  # DEBUG

    def close(self):
        print_info()  # DEBUG
        self.send('STOP')

    def run(self):
        for msg in iter(self.queue.get, 'STOP'):
            pass


class PrintActor(Actor):
    def run(self):
        for msg in iter(self.queue.get, 'STOP'):
            print_info(f"got: {msg}")  # DEBUG


if __name__ == '__main__':

    pa = PrintActor()
    pa.start()
    pa.send("Hello")
    time.sleep(2)
    pa.send("...World!")
    time.sleep(2)
    pa.close()
    pa.join()

输出:

<MainThread, send> sent: Hello
<Thread-1, run> got: Hello
<MainThread, send> sent: ...World!
<Thread-1, run> got: ...World!
<MainThread, close> 
<MainThread, send> sent: STOP