Python异步回调和生成器

时间:2009-11-26 22:32:10

标签: python asynchronous generator

我正在尝试将同步库转换为使用内部异步IO框架。我有几种方法看起来像这样:

def foo:
  ....
  sync_call_1()   # synchronous blocking call
  ....
  sync_call_2()   # synchronous blocking call
  ....
  return bar

对于每个同步函数(sync_call_*),我编写了一个相应的异步函数来进行回调。 E.g。

def async_call_1(callback=none):
  # do the I/O
  callback()

现在对于python新手问题 - 最简单的方法是翻译现有方法以使用这些新的异步方法吗?也就是说,上面的方法foo()现在需要:

def async_foo(callback):
  # Do the foo() stuff using async_call_*
  callback()

一个明显的选择是将回调传递给每个异步方法,该方法有效地“恢复”调用“foo”函数,然后在方法的最后调用回调全局。但是,这会使代码变得脆弱,丑陋,我需要为每次调用async_call_*方法添加一个新的回调。

有没有一种简单的方法可以使用python习语,例如生成器或协同程序?

3 个答案:

答案 0 :(得分:10)

UPDATE :带着一点点盐,因为我与现代python异步开发失去联系,包括geventasyncio,实际上并没有对异步代码有丰富的经验。


在Python中有三种常见的无线程异步编码方法:

  1. 回调 - 丑陋但可行,Twisted做得很好。

  2. 生成器 - 很好但需要所有代码才能遵循风格。

  3. 将Python实现与真正的tasklet一起使用 - Stackless(RIP)和greenlet

  4. 不幸的是,理想情况下整个程序应该使用一种风格,否则事情会变得复杂。如果您的库可以显示完全同步的接口,那么您可能没问题,但如果您希望多次调用库并行工作,尤其是与其他异步代码并行执行,那么您需要一个可以处理所有代码的常见事件“reactor”。

    因此,如果您在应用程序中拥有(或期望用户拥有)其他异步代码,那么采用相同的模型可能很聪明。

    如果您不想了解整个混乱,请考虑使用糟糕的旧线程。它们也很难看,但与其他一切一起工作。

    如果你想了解协同程序如何帮助你 - 以及它们如何使你复杂化,David Beazley's "A Curious Course on Coroutines and Concurrency"是好事。

    如果您可以使用扩展名,那么

    Greenlets可能是最干净的方式。我对他们没有任何经验,所以不能说太多。

答案 1 :(得分:2)

多路复用任务有多种方法。如果没有更深入的了解你正在做的事情,我们不能说你的情况最好。可能最简单/通用的方法是使用线程。请查看this question了解一些想法。

答案 2 :(得分:2)

您还需要使函数foo也异步。这种方法怎么样?

@make_async
def foo(somearg, callback):
    # This function is now async. Expect a callback argument.
    ...

    # change 
    #       x = sync_call1(somearg, some_other_arg)
    # to the following:
    x = yield async_call1, somearg, some_other_arg
    ...

    # same transformation again
    y = yield async_call2, x
    ...

    # change
    #     return bar
    # to a callback call
    callback(bar)

make_async可以像这样定义:

def make_async(f):
    """Decorator to convert sync function to async
    using the above mentioned transformations"""
    def g(*a, **kw):
        async_call(f(*a, **kw))
    return g

def async_call(it, value=None):
    # This function is the core of async transformation.

    try: 
        # send the current value to the iterator and
        # expect function to call and args to pass to it
        x = it.send(value)
    except StopIteration:
        return

    func = x[0]
    args = list(x[1:])

    # define callback and append it to args
    # (assuming that callback is always the last argument)

    callback = lambda new_value: async_call(it, new_value)
    args.append(callback)

    func(*args)

注意:我没有测试过这个