假设我有一些异步运行的任务。它们可能是完全独立的,但是我仍然想设置Taks暂停的位置,以便它们可以同时运行。
同时运行任务的正确方法是什么?我目前正在使用await asyncio.sleep(0)
,但是我觉得这增加了很多开销。
import asyncio
async def do(id, amount):
for i in range(amount):
# Do some time-expensive work
print(f'{id}: has done {i}')
await asyncio.sleep(0)
return f'{id}: done'
async def main():
res = await asyncio.gather(do('Task1', 5), do('Task2', 3))
print(*res, sep='\n')
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
Task1: has done 0
Task2: has done 0
Task1: has done 1
Task2: has done 1
Task1: has done 2
Task1: done
Task2: done
如果我们使用简单的生成器,则空的yield
会暂停任务的执行而没有任何开销,但是空的await
是无效的。
在没有开销的情况下设置此类断点的正确方法是什么?
答案 0 :(得分:3)
如评论中所述,通常异步协程会在可能阻塞或休眠等效同步咖啡的呼叫上自动挂起。在您的情况下,协程是受CPU约束的,因此仅等待阻塞调用是不够的,因此它需要偶尔将控制权放到事件循环中,以允许系统的其余部分运行。
在协同多任务处理中,显式收益并不罕见,为此使用await asyncio.sleep(0)
将work as intended带来风险:睡眠过多,并且通过不必要的切换来减慢计算速度;很少睡觉,而通过在一个协程中花费太多时间来拖累事件循环。
asyncio提供的解决方案是使用run_in_executor
将CPU绑定的代码卸载到线程池中。等待它会自动暂停协程,直到完成CPU密集型任务为止,而不会进行任何中间轮询。例如:
import asyncio
def do(id, amount):
for i in range(amount):
# Do some time-expensive work
print(f'{id}: has done {i}')
return f'{id}: done'
async def main():
loop = asyncio.get_event_loop()
res = await asyncio.gather(
loop.run_in_executor(None, do, 'Task1', 5),
loop.run_in_executor(None, do, 'Task2', 3))
print(*res, sep='\n')
loop = asyncio.get_event_loop()
loop.run_until_complete(main())