基本上我有类似于这样的同步代码:
def f(.):
...
def call(.):
..Some sync code..
try:
resp = f(..)
...some more transformations of resp...
return ..
except:
..Some error handling..
async def af(.):
...
基本上我想动态更改call
的代码,以便它可以调用和等待af
函数而不是f
。有没有办法解决这个问题?我在 github 上找到了syncit
,但这对我来说似乎不是解决方案,因为您必须首先将代码重新设置为异步,然后将其降级以同步。任何帮助将不胜感激。
asyncio
世界中,每个协程都可以在其他协程中执行(使用await
(,也可以通过事件循环阻塞调用(使用run_until_complete()
执行(。
您不能在常规函数中等待协程,因为此函数的调用将是阻塞的,而要等待的协程需要阻塞事件循环执行。这就是asyncio
设计的工作方式。
如果您知道call()
函数被阻塞并且可以访问f()
实现,则可以在运行事件循环中执行协程f()
:
async def af(.):
...
def f(.):
loop = asyncio.get_event_loop()
return loop.run_until_complete(af())
def call(.):
..Some sync code..
try:
resp = f(..)
...some more transformations of resp...
return ..
except:
..Some error handling..
如果您无法访问f()
实现,我相信您将无法更改call()
以等待协程(没有一些丑陋的猴子补丁(。
我认为将call()
重写为异步将是唯一不错的选择。
下面是混合同步和异步代码的两个选项。
鉴于
import asyncio
import trio
from unsync import unsync
法典
选项 1-trio
给定同步代码 (call
(,通过trio
(async_div
调用异步函数:
# Sync code
def call(x, y):
"""Return an awaited result."""
try:
resp = async_div(x, y)
except ZeroDivisionError:
return "Caught an exception."
return resp
# Wrapper
def async_div(*args):
"""Return results from an async function."""
return trio.run(_async_div, *args)
# Async code
async def _async_div(x, y):
"""Return an async result."""
await trio.sleep(3)
return x / y
选项 2-unsync
用@unsync
装饰异步代码并调用result()
:
# Sync code
def call(x, y):
"""Return an awaited result."""
try:
resp = async_div(x, y).result()
except ZeroDivisionError:
return "Caught an exception."
return resp
# Async code
@unsync
async def async_div(x, y):
"""Return an async result."""
await asyncio.sleep(3)
return x / y
演示
在规定的延迟(3 秒(后,结果相同:
call(0, 1)
# 0.0
call(1, 0)
# 'Caught an exception.'
参见
一些其他资源:
- 关于
trio
的文档 - Talk Python 采访
trio
创建者 N. Smith,了解更多细节以及他对异步编程的哲学。 - Python 字节第 73 集第 6 项关于快速
unsync
详细信息 - 肯尼迪先生关于使用
unsync
加快请求速度的网络研讨会演示