以下是一些说明我问题的代码:
def blocking1():
while True:
yield 'first blocking function example'
def blocking2():
while True:
yield 'second blocking function example'
for i in blocking1():
print 'this will be shown'
for i in blocking2():
print 'this will not be shown'
我有两个包含while True
循环的函数。这些将生成数据,然后我会在某处(最有可能是sqlite数据库)登录。
我一直在玩线程并让它运转起来。但是,我真的不喜欢它...我想做的是使我的阻塞函数异步。类似的东西:
def blocking1(callback):
while True:
callback('first blocking function example')
def blocking2(callback):
while True:
callback('second blocking function example')
def log(data):
print data
blocking1(log)
blocking2(log)
我如何在Python中实现这一目标?我已经看到标准库带有asyncore,这个游戏中的大牌是Twisted,但这两个似乎都用于套接字IO。
如何异步我的非套接字相关阻塞函数?
答案 0 :(得分:29)
阻止功能是一种不返回的功能,但仍会使您的进程处于空闲状态 - 无法完成更多工作。
您要求我们将您的阻止功能设为非阻止功能。但是 - 除非您正在编写操作系统 - 您没有任何阻止功能。您可能具有阻塞函数,因为它们调用阻塞系统调用,或者您可能具有“阻塞”的函数,因为它们会进行大量计算。
在不使基础系统调用非阻塞的情况下,使前一类函数无阻塞是不可能的。根据系统调用的内容,如果不向程序添加事件循环,可能很难使其无阻塞;你不仅需要拨打电话并且不阻止它,你还必须再打一次电话,以确定该呼叫的结果将在你可以关联的地方传递。
这个问题的答案是一个非常长的python程序和许多不同操作系统界面的解释以及它们如何工作,但幸运的是我已经在不同的网站上写了这个答案;我叫它Twisted。如果您的特定任务已经supported by a Twisted reactor,那么您很幸运。否则,只要您的任务映射到某些现有操作系统概念,您就可以扩展反应器以支持它。实际上,这些机制中只有两种:每个合理操作系统上的文件描述符,以及Windows上的I / O完成端口。
在另一种情况下,如果你的功能消耗了大量CPU,因此没有返回,那么它们并没有真正阻塞;你的过程仍在继续,并完成工作。有三种方法可以解决这个问题:
在Twisted中,这最后一种技术可以通过各种方式实现,但这是一种语法上方便的技巧,使其变得简单:
from twisted.internet import reactor
from twisted.internet.task import deferLater
from twisted.internet.defer import inlineCallbacks, returnValue
@inlineCallbacks
def slowButSteady():
result = SomeResult()
for something in somethingElse:
result.workHardForAMoment(something)
yield deferLater(reactor, 0, lambda : None)
returnValue(result)
答案 1 :(得分:9)
您可以使用生成器进行协作式多任务处理,但您必须编写自己的主循环,以便在它们之间传递控制权。
以下是使用上述示例的一个(非常简单)示例:
def blocking1():
while True:
yield 'first blocking function example'
def blocking2():
while True:
yield 'second blocking function example'
tasks = [blocking1(), blocking2()]
# Repeat until all tasks have stopped
while tasks:
# Iterate through all current tasks. Use
# tasks[:] to copy the list because we
# might mutate it.
for t in tasks[:]:
try:
print t.next()
except StopIteration:
# If the generator stops, remove it from the task list
tasks.remove(t)
您可以通过允许生成器生成新的生成器来进一步改进它,然后可以将其添加到任务中,但希望这个简化的示例能够提供一般的想法。
答案 2 :(得分:2)
扭曲的框架不仅仅是套接字。它具有适用于许多场景的异步适配器,包括与子进程交互。我建议仔细看看。它会做你想做的事。
答案 3 :(得分:1)
答案 4 :(得分:0)
您的代码没有阻止。 blocking1()和它的兄弟立即返回迭代器(不阻塞),也没有一个迭代块(在你的情况下)。
如果你想从两个迭代器中逐个“吃”,不要让你的程序在继续之前尝试完全吃掉“blocking1()”......
for b1, b2 in zip(blocking1(), blocking2()):
print 'this will be shown', b1, 'and this, too', b2