Python - 如何使这段代码异步?

时间:2011-02-11 06:12:20

标签: python asynchronous twisted nonblocking event-driven

以下是一些说明我问题的代码:

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。

如何异步我的非套接字相关阻塞函数?

5 个答案:

答案 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)

如果您不想使用完整的操作系统线程,可以尝试使用Stackless,这是Python的一种变体,它添加了许多有趣的功能,包括“微线程”。有很多好的examples你会发现有用。

答案 4 :(得分:0)

您的代码没有阻止。 blocking1()和它的兄弟立即返回迭代器(不阻塞),也没有一个迭代块(在你的情况下)。

如果你想从两个迭代器中逐个“吃”,不要让你的程序在继续之前尝试完全吃掉“blocking1()”......

for b1, b2 in zip(blocking1(), blocking2()):
    print 'this will be shown', b1, 'and this, too', b2