我正在尝试将同步库转换为使用内部异步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习语,例如生成器或协同程序?
答案 0 :(得分:10)
UPDATE :带着一点点盐,因为我与现代python异步开发失去联系,包括gevent和asyncio,实际上并没有对异步代码有丰富的经验。
在Python中有三种常见的无线程异步编码方法:
回调 - 丑陋但可行,Twisted做得很好。
生成器 - 很好但需要所有代码才能遵循风格。
将Python实现与真正的tasklet一起使用 - Stackless(RIP)和greenlet。
不幸的是,理想情况下整个程序应该使用一种风格,否则事情会变得复杂。如果您的库可以显示完全同步的接口,那么您可能没问题,但如果您希望多次调用库并行工作,尤其是与其他异步代码并行执行,那么您需要一个可以处理所有代码的常见事件“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)
注意:我没有测试过这个