如何使用Python Asyncio在异步pgAPI上实现同步外观?
问题描述
设想一个异步aiohttp
Web应用程序,该应用程序由通过asyncpg
连接的PostgreSQL数据库支持,并且没有其他I/O。如何才能有一个托管应用程序逻辑的中间层,即而不是异步?(我知道我可以简单地使所有东西都异步--但是想象一下我的应用程序有大量的应用程序逻辑,仅由数据库I/O绑定,并且我不能触及它的所有内容)。
伪码:
async def handler(request):
# call into layers over layers of application code, that simply emits SQL
...
def application_logic():
...
# This doesn't work, obviously, as await is a syntax
# error inside synchronous code.
data = await asyncpg_conn.execute("SQL")
...
# What I want is this:
data = asyncpg_facade.execute("SQL")
...
如何在asyncpg
上构建允许应用程序逻辑进行数据库调用的同步外观?在这种情况下,使用async.run()
或asyncio.run_coroutine_threadsafe()
等浮动的食谱不起作用,因为我们来自一个已经异步的上下文。我假设这不是不可能的,因为原则上已经有一个事件循环可以运行asyncpg
协程。
额外问题:使await
内部同步成为语法错误的设计原理是什么?允许await
来自任何源自协程的上下文不是很有用吗?这样我们就有了在功能构建块中分解应用程序的简单方法?
编辑额外的好处:除了Paul's very good answer之外,我对避免阻塞主线程的解决方案感兴趣(产生更多类似gevent的东西)。另请参阅我对保罗回答的评论.
解决方案
您需要创建在其中运行异步代码的辅助线程。您可以使用其自己的事件循环来初始化辅助线程,该事件循环将永远运行。通过调用run_coroutine_ThreadSafe()并对返回的对象调用result()来执行每个异步函数。这是concurrent.futures.Future的一个实例,它的result()方法直到协程的结果从第二个线程准备好之后才返回。
这样,您的主线程实际上就像调用同步函数一样调用每个异步函数。在每个函数调用完成之前,主线程不会继续。顺便说一下,同步函数是否在事件循环上下文中实际运行并不重要。对result()的调用当然会挡路主线程的事件循环。如果您想要获得从同步代码运行异步函数的效果,这是无法避免的。
不用说,这是一件难看的事情,而且它暗示了错误的程序结构。但您正在尝试转换旧版程序,这可能会对此有所帮助。import asyncio
import threading
from datetime import datetime
def main():
def thr(loop):
asyncio.set_event_loop(loop)
loop.run_forever()
loop = asyncio.new_event_loop()
t = threading.Thread(target=thr, args=(loop, ), daemon=True)
t.start()
print("Hello", datetime.now())
t1 = asyncio.run_coroutine_threadsafe(f1(1.0), loop).result()
t2 = asyncio.run_coroutine_threadsafe(f1(2.0), loop).result()
print(t1, t2)
if __name__ == "__main__":
main()
>>> Hello 2021-10-26 20:37:00.454577
>>> Hello 1.0 2021-10-26 20:37:01.464127
>>> Hello 2.0 2021-10-26 20:37:03.468691
>>> 1.0 2.0
相关文章