执行协程
asyncio提供了三种执行协程的机制
- 使用asyncio.run()执行协程(仅限于python3.7以上版本)。此函数总是创建一个新的事件循环,并在最后关闭它。建议将它用作asyncio程序的主入口,如main(),并且只调用一次。在同一个线程中,当已经有asyncio事件循环在执行时,不能调用此函数。
- await一个协程。在一个已经运行协程中await另一协程。
import asyncio
async def do_some_work(x):
print("Hello:", x)
return "work is done for {}".format(x)
# async def main():
# tasks = []
# for i in range(5):
# tasks.append(asyncio.ensure_future(do_some_work(i)))
# return await asyncio.gather(*tasks)
async def main():
for i in range(5):
result = await do_some_work(i)
print(result)
asyncio.run(main())
Hello: 0
work is done for 0
Hello: 1
work is done for 1
Hello: 2
work is done for 2
Hello: 3
work is done for 3
Hello: 4
work is done for 4
之前使用的是python 3.6版本,可以在外部直接await
await main()
Hello: 0
Hello: 1
Hello: 2
Hello: 3
Hello: 4
Out[34]:
['work is done for 0',
'work is done for 1',
'work is done for 2',
'work is done for 3',
'work is done for 4']
但是在3.7里,就报错。。。
await main()
File "<input>", line 14
SyntaxError: 'await' outside function
- 定义一个事件循环对象loop容器,将task任务扔进事件循环对象中触发。把协程对象交给 loop,协程对象随后会在 loop 里得到运行。
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_until_complete(main())
Hello: 0
work is done for 0
Hello: 1
work is done for 1
Hello: 2
work is done for 2
Hello: 3
work is done for 3
Hello: 4
work is done for 4
停止协程
loop启动之后,ctrl+c,run_until_complete会有执行异常。通过asyncio.Task的cancel方法可以取消future。
调用cancel()会引起Task对象向被封装的协程抛出CancelledError异常。当取消行为发生时,如果协程正在等待某个Future对象执行,该Future对象将被取消。
cancelled()方法用于检查某个Task是否已被取消。如果Task封装的协程没有阻止CancelledError异常,且Task确实被取消了,则该方法返回True。
import asyncio
import time
async def do_some_work(x):
print('Hello do_some_work:', x)
try:
await asyncio.sleep(x)
except asyncio.CancelledError:
print('task do_some_work({}) was canceled'.format(x))
raise # 这里不raise的话,程序会继续,task 仍然是active(这是try, except特征决定的)
print('do_some_work({}) is still active'.format(x))# 如果这句话被打印,证明task仍是active
return "work is done for {}".format(x)
def cancel_task(future):
future.cancel()
print('cancelled the task:', id(future))
async def main(loop):
tasks = []
for i in range(5):
coroutine = do_some_work(i)
# task = asyncio.ensure_future(coroutine)
task = loop.create_task(coroutine)
tasks.append(task)
# loop.call_soon(cancel_task, task)
return await asyncio.gather(*tasks)
print('start...')
start = time.time()
event_loop = asyncio.new_event_loop()
asyncio.set_event_loop(event_loop)
try:
results = event_loop.run_until_complete(main(event_loop))
for result in results:
print("task result:", result)
except KeyboardInterrupt:
now = event_loop.time()
# print(asyncio.Task.all_tasks())
for task in asyncio.Task.all_tasks():
print('cancelling the task {}: {}'.format(id(task), task.cancel()))
# event_loop.call_soon(cancel_task, task)
event_loop.stop()
event_loop.run_forever() # restart loop
finally:
event_loop.close()
end = time.time()
print("total run time: ", end - start)
start...
Hello do_some_work: 0
Hello do_some_work: 1
Hello do_some_work: 2
Hello do_some_work: 3
Hello do_some_work: 4
do_some_work(0) is still active
do_some_work(1) is still active
^Ccancelling the task 2246916857928: True
cancelling the task 2246916861192: True
cancelling the task 2246916858200: False
cancelling the task 2246916860240: True
cancelling the task 2246916861328: True
cancelling the task 2246916860376: False
task do_some_work(2) was canceled
task do_some_work(3) was canceled
task do_some_work(4) was canceled
total run time: 2.0016472339630127
True 表示 cancel 成功。运行程序,不到一秒的时间立马就ctr+c。以上看出 do_some_work(0) 没有cancel成功,因为asyncio.sleep(0) 也就是没有sleep, 所以do_some_work(0)瞬间就完成了,这时候取消为时已晚,其他没有来得及执行的task都成功取消了。
shield
如果一个创建后就不希望被任何情况取消,可以使用asyncio.shield保护任务能顺利完成。
shield方法可以将协程(coroutines),该对象会被自动封装为Task。