如果我使用异步函数,那么堆栈上方的所有函数也应该是异步的,并且它们的调用应以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()
当执行线程遇到“ await”时,它可以切换到另一个协程,这需要用于上下文切换的资源。由于存在大量相互竞争的规则和不同级别的抽象,这些开销可能开始限制整个系统的性能。但是在给出的示例中,仅在一种情况下在线切换上下文是有意义的:
await asyncio.sleep(1)
如何禁止某些异步功能的上下文切换?
答案 0 :(得分:4)
首先,默认情况下,在您的示例上下文中不会切换。换句话说,在协程遇到实际受阻的东西(例如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()
在输出中,您将看到:
control inside event loop
before func2
before func1
control inside event loop
control inside event loop
after func1
after func2
control inside event loop
func2
将执行流直接传递到func1
,避免了事件循环的_run_once
可能切换到另一个协程。只有遇到阻塞asyncio.sleep
时,事件循环才得到控制。
尽管它是默认事件循环实现的详细信息。
第二点,也许更重要的是,与使用异步处理I / O所带来的好处相比,在协程之间切换非常便宜。
它也比其他异步替代方案(例如在OS线程之间切换)便宜得多。
由于协程太多而导致代码变慢的情况是不太可能的,但是即使发生这种情况,您也应该研究一下uvloop之类的更有效的事件循环实现。
答案 1 :(得分:0)
我想指出的是,如果您运行足够多的协程以至于切换上下文的开销成为问题,则可以使用Semaphore确保减少并发。最近,我将运行HTTP请求的协程的并发性从1000减少到50,从而使性能提高了约2倍。