从Python中消除异步污染

从Python中消除异步污染,python,python-asyncio,api-design,Python,Python Asyncio,Api Design,在这样的程序中,如何消除异步无处不在的疯狂 import asyncio async def async_coro(): await asyncio.sleep(1) async def sync_func_1(): # This is blocking and synchronous await async_coro() async def sync_func_2(): # This is blocking and synchronous a

在这样的程序中,如何消除异步无处不在的疯狂

import asyncio


async def async_coro():
    await asyncio.sleep(1)


async def sync_func_1():
    # This is blocking and synchronous
    await async_coro()


async def sync_func_2():
    # This is blocking and synchronous
    await sync_func_1()


if __name__ == "__main__":
    # Async pollution goes all the way to __main__
    asyncio.run(sync_func_2())
我需要有3个异步标记和asyncio.run在顶层,以便调用一个异步函数。我假设我做错了什么-我如何清理这段代码以减少使用异步


FWIW,我之所以感兴趣,主要是因为我正在使用asyncio编写一个API,我不希望我的用户不得不根据他们是否使用API的异步部分,过多地考虑他们的函数需要是def还是async def。

经过一些研究,一个答案是手动管理事件循环:

import asyncio


async def async_coro():
    await asyncio.sleep(1)


def sync_func_1():
    # This is blocking and synchronous
    loop = asyncio.get_event_loop()
    coro = async_coro()
    loop.run_until_complete(coro)


def sync_func_2():
    # This is blocking and synchronous
    sync_func_1()


if __name__ == "__main__":
    # No more async pollution
    sync_func_2()

如果您必须这样做,我建议您采用以下方法:

import asyncio, threading

async def async_coro():
    await asyncio.sleep(1)

_loop = asyncio.new_event_loop()
threading.Thread(target=_loop.run_forever, daemon=True).start()

def sync_func_1():
    # This is blocking and synchronous
    return asyncio.run_coroutine_threadsafe(async_coro(), _loop).result()

def sync_func_2():
    # This is blocking and synchronous
    sync_func_1()

if __name__ == "__main__":
    sync_func_2()

与同步函数运行事件循环的方法相比,这种方法的优势在于它支持同步函数的嵌套。它还只运行单个事件循环,因此,如果基础库希望设置(例如,用于监视的后台任务)等,它将连续工作,而不是每次重新生成。

此代码不起作用。您的意思是什么?它运行和睡眠。异步。睡眠不是经典意义上的睡眠。在这种情况下,它为隐式返回None安排一个continuation,并将控制权交还给循环。我的意思是,可以随意调用它,但程序运行需要1秒。如果在任何时候以这种方式实现的一个同步函数需要调用另一个这样的函数,那么它肯定不会做任何不起作用的事情-您将得到一个事件循环正在运行RuntimeError。此外,事件循环的设置和拆卸成本是不可忽略的,如果是在幕后完成的话——在我的机器上,一个空的asyncio.run大约需要0.15毫秒。添加了一个替代答案来避免这个问题。