Phi*_*ing 8 python async-await
我想知道当事件循环切换任务时,python 提供了什么保证。
据我所知async/await从线程显著不同之处在于基于时间分片事件循环不切换任务,这意味着除非任务收益率(await),它会无限期地进行。这实际上很有用,因为在 asyncio 下管理临界区比使用线程更容易。
我不太清楚的是以下内容:
async def caller():
while True:
await callee()
async def callee():
pass
Run Code Online (Sandbox Code Playgroud)
在这个例子中caller是重复的await。所以从技术上讲,它正在屈服。但是我不清楚这是否会允许事件循环上的其他任务执行,因为它只callee屈服于并且永远不会屈服。
也就是说,如果我callee在“关键部分”内等待,即使我知道它不会阻塞,我是否有发生其他意外事件的风险?
Hur*_*ful 10
你保持警惕是对的。caller产生于callee,并产生于事件循环。然后事件循环决定恢复哪个任务。其他任务可能(希望)被挤压在对 的调用之间callee。callee需要等待实际阻塞,Awaitable例如asyncio.Futureor asyncio.sleep(),而不是协程,否则控制将不会返回到事件循环直到caller返回。
例如,以下代码将caller2在开始处理任务之前完成caller1任务。因为callee2本质上是一个同步函数,无需等待阻塞的 I/O 操作,因此,不会创建暂停点,并且caller2会在每次调用callee2.
import asyncio
import time
async def caller1():
for i in range(5):
await callee1()
async def callee1():
await asyncio.sleep(1)
print(f"called at {time.strftime('%X')}")
async def caller2():
for i in range(5):
await callee2()
async def callee2():
time.sleep(1)
print(f"sync called at {time.strftime('%X')}")
async def main():
task1 = asyncio.create_task(caller1())
task2 = asyncio.create_task(caller2())
await task1
await task2
asyncio.run(main())
Run Code Online (Sandbox Code Playgroud)
结果:
sync called at 19:23:39
sync called at 19:23:40
sync called at 19:23:41
sync called at 19:23:42
sync called at 19:23:43
called at 19:23:43
called at 19:23:44
called at 19:23:45
called at 19:23:46
called at 19:23:47
Run Code Online (Sandbox Code Playgroud)
但是如果callee2 awaits如下,即使 awaits 也会发生任务切换asyncio.sleep(0),并且任务会并发运行。
sync called at 19:23:39
sync called at 19:23:40
sync called at 19:23:41
sync called at 19:23:42
sync called at 19:23:43
called at 19:23:43
called at 19:23:44
called at 19:23:45
called at 19:23:46
called at 19:23:47
Run Code Online (Sandbox Code Playgroud)
结果:
called at 19:22:52
sync called at 19:22:52
called at 19:22:53
sync called at 19:22:53
called at 19:22:54
sync called at 19:22:54
called at 19:22:55
sync called at 19:22:55
called at 19:22:56
sync called at 19:22:56
Run Code Online (Sandbox Code Playgroud)
这种行为不一定直观,但考虑到它asyncio是为了同时处理 I/O 操作和网络,而不是通常的同步 Python 代码,这是有道理的。
还有一点要注意的是:这仍然有效,如果callee等待协同程序,反过来,等待一个asyncio.Future,asyncio.sleep()或另一个协程说的那些东西环比下滑的await之一。当Awaitable等待阻塞时,流控制将返回到事件循环。所以以下也有效。
async def callee2():
await inner_callee()
print(f"sync called at {time.strftime('%X')}")
async def inner_callee():
await asyncio.sleep(1)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
775 次 |
| 最近记录: |