python - 调用异步函数时如何防止上下文切换?
问题描述
如果我使用异步函数,那么堆栈上面的所有函数也应该是异步的,并且它们的调用应该在 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'时,它可以切换到另一个协程,这需要资源进行上下文切换。随着大量竞争的corutes和不同的抽象级别,这些开销可能会开始限制整个系统的性能。但是在给出的示例中,仅在一种情况下在线切换上下文是有意义的:
await asyncio.sleep(1)
如何禁止某些异步函数的上下文切换?
解决方案
首先,默认情况下,在您的示例上下文中不会切换。换句话说,直到协程遇到实际阻塞的事情(例如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
遇到阻塞时,事件循环才能获得控制权。
虽然这是默认事件循环的实现细节。
其次,可能更重要的是,与使用 asyncio 处理 I/O 相比,协程之间的切换非常便宜。
它也比其他异步替代方案(如在 OS 线程之间切换)便宜得多。
由于许多协程而导致代码变慢的情况极不可能发生,但即使发生这种情况,您也应该看看更有效的事件循环实现,例如uvloop。
推荐阅读
- python - 数组的所有子组合之间的差异,最有效的方法
- android-studio - 亚行连接到
不再工作或无休止的安装过程 - python - 如何将尼泊尔语或印地语文本呈现为标签?
- docker - Kubernetes Pods `ErrImagePull` 是什么意思?
- ruby-on-rails - 由于出错,Ruby gem 'byebug 5.0.0' 安装失败
- objective-c - 为什么 CFStream 不使用 TLS 1.3?
- maps - 对具有不同行数的数据集应用 transform_lookup
- html - 是否可以使用渐变更改网站的背景颜色
- python-3.x - 无一例外地尝试每一行过早地破坏代码
- docker - 在 Sitecore 10 安装期间收到不健康的容器消息