我对如何在python / twisted中编写异步代码感到有点困惑。假设(为了论证)我向世界公开了一个函数,它接受一个数字并返回True / False,如果它是素数/非素数,所以它看起来像这样模糊:
def IsPrime(numberin):
for n in range(2,numberin):
if numberin % n == 0: return(False)
return(True)
(只是为了说明)。
现在假设有一个网络服务器需要根据提交的值调用IsPrime。大numberin
需要很长时间。
如果同时另一个用户询问一个小数的素数,是否有办法使用reactor / deferreds架构异步运行两个函数调用,以便在结果之前返回短计算结果长期计算?
如果IsPrime功能来自我的网络服务器将执行延迟的getPage的其他网络服务器,我理解如何执行此操作,但如果它只是一个本地函数呢?
即,可以在两次调用IsPrime之间以某种方式进行时间共享,还是需要显式调用新线程?
或者,IsPrime循环是否需要分成一系列较小的循环,以便控制可以快速传递回反应堆?
还是其他什么?
答案 0 :(得分:26)
我认为你目前的理解基本上是正确的。 Twisted只是一个Python库,你编写的Python代码正常执行,就像你期望的Python代码一样:如果你只有一个线程(和一个进程),那么一次只能发生一件事。 Twisted提供的API几乎没有创建新的线程或进程,因此在正常情况下,您的代码会按顺序运行; isPrime
在第一次完成执行后才能再次执行。
仍在考虑单个线程(和单个进程),Twisted的所有“并发”或“并行性”来自这样一个事实,即不是阻塞网络I / O(以及某些其他阻塞操作),Twisted提供用于以非阻塞方式执行操作的工具。这使得程序可以继续执行其他工作,否则可能无法等待阻塞I / O操作(例如读取或写入套接字)以完成。
通过将它们分成小块并让事件处理程序在这些块之间运行,可以使事物“异步”。如果转换不会使代码更难理解和维护,这有时是一种有用的方法。 Twisted提供了一个帮助程序来安排这些工作块cooperate
。使用此帮助程序是有益的,因为它可以根据所有不同的工作源做出调度决策,并确保在没有显着额外延迟的情况下有剩余时间来服务事件源(换句话说,您添加的作业越多) ,每个工作得到的时间越少,以便反应堆能够继续完成其工作。)
Twisted还提供了几个用于处理线程和进程的API。如果不明显如何将作业分成块,这些可能很有用。您可以使用deferToThread
在线程池中运行(线程安全!)函数。方便的是,此API返回Deferred
,最终将使用函数的返回值触发(如果函数引发异常,则使用Failure
)。这些延迟看起来像任何其他的,并且就使用它们的代码而言,它也可以从像getPage
这样的调用回来 - 一个不使用额外线程的函数,只是非阻塞I / O和事件处理程序。
由于Python不适合在单个进程中运行多个CPU绑定线程,因此Twisted还提供了一个非阻塞API,用于启动子进程并与子进程通信。您可以将计算卸载到此类进程以利用额外的CPU或核心,而无需担心GIL会降低您的速度,这既不是分块策略也不是线程方法提供的。处理此类流程的最低级API是reactor.spawnProcess
。还有Ampoule,这个包将为您管理流程池,并为流程deferToAMPProcess
提供deferToThread
的模拟。