Django — async_to_sync 与 asyncio.run
Posted
技术标签:
【中文标题】Django — async_to_sync 与 asyncio.run【英文标题】:Django — async_to_sync vs asyncio.run 【发布时间】:2019-12-27 17:02:05 【问题描述】:我们可以使用这两个函数来同步运行任何异步函数:
import asyncio
from asgiref.sync import async_to_sync
asyncio.run(asyncio.sleep(1))
async_to_sync(asyncio.sleep)(1)
有什么区别?我们可以总是使用asyncio.run
代替async_to_sync
吗?
【问题讨论】:
【参考方案1】:区别
他们有不同的目的。 async_to_sync
将 awaitable 转换为同步可调用对象,asyncio.run
执行协程并返回结果。
根据documentation,来自async_to_sync
的可调用对象在子线程中工作。
async_to_sync
不会为每个线程创建事件循环,以防您在由sync_to_async
生成并在异步代码中运行的同步代码中。它重用了一个异步代码循环。举个例子:
import asyncio
from asgiref.sync import async_to_sync, sync_to_async
async def running(n):
return [await sync_to_async(sync)(i) for i in range(n)]
def sync(n):
# it will create a new loop for every call
return asyncio.run(from_sync(n))
async def from_sync(n):
return n
print("Result:", asyncio.run(running(3)))
这将运行 4 个循环:1 次调用 running
,3 次调用 from_sync
。
如果我们在sync
中使用async_to_sync
而不是asyncio.run
,我们会将循环数减少到1 以调用running
。
要查看它,您可以包装 new_event_loop
函数:
def print_deco(fn, msg):
def inner():
res = fn()
print(msg, res)
return res
return inner
p = asyncio.get_event_loop_policy()
p.new_event_loop = print_deco(p.new_event_loop, "NEW EVENT LOOP:")
你可以在这个post找到详细的解释。
【讨论】:
如果我使用asyncio.run
而不是async_to_sync
运行异步代码会发生什么?
@MaxMalyshIReinstateMonica 如the documentation 中所述,您可能会收到SynchronousOnlyOperation 错误(从django 3.0 开始),可以使用os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true"
禁用它,但也有警告:“如果您启用此选项并且可以并行访问 Django 的异步不安全部分,您可能会遭受数据丢失或损坏”。
只有当我们在异步函数中进行 同步 调用时才会发生这种情况(例如,如果我们将在事件循环中调用 django ORM)。这就是为什么我们需要将同步调用包装到 sync_to_async 中。我的问题是关于异步 ---> 同步,反之亦然。
@MaxMalyshIReinstateMonica 你是对的。我已经更新了答案以上是关于Django — async_to_sync 与 asyncio.run的主要内容,如果未能解决你的问题,请参考以下文章
Django Channels 是不是会在每次使用时创建一个新的消费者?