调用异步函数时如何防止上下文切换?

Sem*_*nDr 5 python asynchronous python-3.x async-await python-asyncio

如果我使用异步函数,那么堆栈上方的所有函数也应该是异步的,并且其调用之前应带有await关键字。此示例使用应用程序的多个体系结构层模拟现代程序:

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

async def func2():
    await func1()

async def func3():
    await func2()

async def func4():
    await func3()

async def func5():
    await func4()
Run Code Online (Sandbox Code Playgroud)

当执行线程遇到“等待”时,它可以切换到另一个协程,这需要用于上下文切换的资源。由于存在大量相互竞争的规则和不同级别的抽象,这些开销可能开始限制整个系统的性能。但是在给出的示例中,仅在一种情况下在线切换上下文是有意义的:

await asyncio.sleep(1)
Run Code Online (Sandbox Code Playgroud)

如何禁止某些异步功能的上下文切换?

Mik*_*mov 8

首先,默认情况下,在您的示例上下文中不会切换。换句话说,在协程Future遇到实际阻塞的东西(如)之前,它不会将控制权返回到事件循环,而直接恢复到内部协程。

我不知道比继承默认事件循环实现更简单的方法来演示这一点:

import asyncio


class TestEventLoop(asyncio.SelectorEventLoop):
    def _run_once(self):
        print('control inside event loop')
        super()._run_once()


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


async def func2():
    print('before func1')
    await func1()
    print('after func1')


async def main():
    print('before func2')
    await func2()
    print('after func2')


loop = TestEventLoop()
asyncio.set_event_loop(loop)
try:
    loop.run_until_complete(main())
finally:
    loop.close()
Run Code Online (Sandbox Code Playgroud)

在输出中,您将看到:

control inside event loop
before func2
before func1
control inside event loop
control inside event loop
after func1
after func2
control inside event loop
Run Code Online (Sandbox Code Playgroud)

func2直接传递执行流来func1避免_run_once可能切换到另一个协程的事件循环。仅当asyncio.sleep遇到阻塞时,事件循环才得到控制。

尽管它是默认事件循环实现的详细信息。


其次,也许更重要的是,与使用异步处理I / O所获得的好处相比,在协程之间进行切换非常便宜。

它也比其他异步替代方法(例如,在OS线程之间切换)便宜得多。

由于协程太多而导致代码变慢的情况极不可能发生,但是即使发生了这种情况,您也应该研究一下更有效的事件循环实现,例如uvloop