【问题标题】:Python asynchronous callbacks and generatorsPython 异步回调和生成器
【发布时间】:2010-12-20 19:53:14
【问题描述】:

我正在尝试将同步库转换为使用内部异步 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 asynchronous generator


    【解决方案1】:

    多路复用任务有多种方法。如果不深入了解您在做什么,我们无法说出最适合您的情况。可能最简单/通用的方法是使用线程。看看this question 了解一些想法。

    【讨论】:

      【解决方案2】:

      更新:对此持保留态度,因为我与现代 python 异步开发脱节,包括 geventasyncio 并且实际上并没有认真的经验异步代码。


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

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

      2. 生成器 - 不错,但需要所有您的代码来遵循样式。

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

      不幸的是,理想情况下,整个程序应该使用一种风格,否则事情会变得复杂。如果你对你的库公开一个完全同步的接口没问题,你可能没问题,但是如果你希望对你的库的多个调用并行工作,特别是与 other 异步代码并行工作,那么你需要一个可以处理所有代码的通用事件“反应器”。

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

      如果您不想了解整个混乱,请考虑使用糟糕的旧线程。它们也很丑陋,但可以与其他所有东西一起使用。

      如果您确实想了解协程如何帮助您 - 以及它们如何使您复杂化,David Beazley's "A Curious Course on Coroutines and Concurrency" 是个好东西。

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

      【讨论】:

        【解决方案3】:

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

        注意:我还没有测试过这个

        【讨论】:

          猜你喜欢
          • 2013-03-07
          • 2019-11-16
          • 2018-11-24
          • 1970-01-01
          • 2016-05-20
          • 2015-05-30
          • 2021-11-19
          • 1970-01-01
          • 2021-08-24
          相关资源
          最近更新 更多