Python 的 Twisted Reactor 是如何工作的?

How does Python's Twisted Reactor work?

最近,我一直在研究 Twisted 文档。根据我收集到的信息,Twisted 功能的基础是它称为 "Reactor" 的事件循环的结果。反应器侦听某些事件并将它们分派给已设计用于处理这些事件的已注册回调函数。在书中,有一些伪代码描述了 Reactor 的作用,但我很难理解它,它对我来说没有任何意义。

 while True:
     timeout = time_until_next_timed_event()
     events = wait_for_events(timeout)
     events += timed_events_until(now())
     for event in events:
         event.process()

这是什么意思?

我会尽量详细说明:

  • 程序让出控制权并在等待事件时进入休眠状态。 我想这里最有趣的部分是事件。 事件是: 根据外部需求(接收网络数据包、点击键盘、计时器、不同的程序调用)程序接收控制(在其他线程或 在特殊程序中)。 wait_for_events 中的睡眠不知何故被打断 wait_for_events returns.

  • 在控件出现时,事件处理程序将该事件的信息存储到某个数据结构中,events,稍后用于对此做一些事情事件事件->进程)。 在wait_for_events的进入和退出之间的时间里,可能发生的事件不止一个,而是很多,必须全部处理。 event->process() 过程是自定义的,通常应该调用有趣的部分 - 用户的扭曲代码。

如果它不明显,它被称为 反应器 因为它 反应 事情。循环是它如何反应。

一次一行:

while True:

实际上while True;它更像是 while not loop.stopped。您可以调用 reactor.stop() 来停止循环,并且(在执行一些关闭逻辑之后)循环实际上会退出。但它在示例中被描述为 while True 因为当您编写一个长期存在的程序时(就像您经常使用 Twisted 一样)最好假设您的程序将崩溃或永远 运行 , "cleanly exiting" 并不是一个真正的选择。

     timeout = time_until_next_timed_event()

如果我们稍微扩展一下这个计算,它可能更有意义:

def time_until_next_timed_event():
    now = time.time()
    timed_events.sort(key=lambda event: event.desired_time)
    soonest_event = timed_events[0]
    return soonest_event.desired_time - now

timed_events 是用 reactor.callLater 安排的事件列表;即应用程序在特定时间请求 Twisted 到 运行 的功能。

     events = wait_for_events(timeout)

这里的这一行是 Twisted 的 "magic" 部分。我无法以一般方式扩展 wait_for_events,因为它的实现完全取决于操作系统如何使所需事件可用。而且,鉴于操作系统是复杂而棘手的野兽,我无法以特定的方式对其进行扩展,同时保持其足够简单以回答您的问题。

这个函数的意思是,要求操作系统或它的 Python 包装器阻塞,直到一个或多个先前向它注册的对象 - 至少,东西例如侦听端口和已建立的连接,但也可能是按钮之类的东西可能会被点击 - 是 "ready for work"。当它们从网络到达时,工作可能是从套接字中读取一些字节。当缓冲区足够清空时,工作可能是将字节写入网络。它可能正在接受一个新连接或处理一个已关闭的连接。这些可能的事件中的每一个都是 reactor 可能调用您的对象的函数:dataReceivedbuildProtocolresumeProducing 等,如果您阅读完整的 Twisted 教程,您将了解到这些。

一旦我们得到假设的 "event" 对象列表,每个对象都有一个假想的“process”方法(方法的确切名称在反应器中不同,只是由于历史的意外),然后我们回到处理时间:

     events += timed_events_until(now())

首先,假设 events 只是抽象 Event class 的 list,每个特定类型都有一个 process 方法事件需要填写。

此时循环已经"woken up",因为wait_for_events,停止了阻塞。但是,根据 "asleep" for 的时间长短,我们不知道可能需要执行多少个定时事件。如果没有任何事情发生,我们可能会一直睡到超时,但如果有很多连接处于活动状态,我们可能根本就没有睡过。因此,我们检查当前时间 ("now()"),并将每个定时事件添加到我们需要处理的事件列表中,每个定时事件的 desired_time 是在当前时间或之前。

最后,

     for event in events:
         event.process()

这只是意味着 Twisted 遍历了它必须做的事情列表并完成了它们。实际上它当然会处理每个事件的异常,并且反应堆的具体实现通常只是直接调用事件处理程序而不是创建一个类似于 Event 的对象来记录需要首先完成的工作,但是从概念上讲,这就是发生的事情。例如,这里的 event.process 可能意味着调用 socket.recv() 然后 yourProtocol.dataReceived 结果。

我希望这个扩展的解释能帮助您理解它。如果您想通过研究 Twisted 了解更多信息,我鼓励您 join the mailing list, hop on to the IRC channel, #twisted to talk about applications or #twisted-dev to work on Twisted itself, both on Freenode.