繁体   English   中英

Python异步回调和生成器

[英]Python asynchronous callbacks and generators

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

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

对于每个同步函数( sync_call_* ),我编写了一个相应的异步函数,它接受一个回调。 例如

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成语,例如生成器或协同程序?

更新 :由于我与现代python异步开发(包括geventasyncio)脱节,并且实际上对异步代码没有认真的经验,因此我带着一点 点盐


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

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

  2. 生成器 - 很好,但要求所有代码都遵循这种风格。

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

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

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

如果你不想了解整个混乱,可以考虑使用糟糕的旧线程。 它们也很丑陋,但与其他一切一起工作。

如果你想了解协同程序如何帮助你 - 以及它们如何使你复杂化, David Beazley的“关于协同程序和并发的好奇课程”是很好的东西。

如果你可以使用扩展名, Greenlets可能是最干净的方式。 我对他们没有任何经验,所以不能说太多。

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

你需要使函数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)

小心:我没有测试过这个

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM