python异步编程案例之协程执行和停止

执行协程
asyncio提供了三种执行协程的机制

  1. 使用asyncio.run()执行协程(仅限于python3.7以上版本)。此函数总是创建一个新的事件循环,并在最后关闭它。建议将它用作asyncio程序的主入口,如main(),并且只调用一次。在同一个线程中,当已经有asyncio事件循环在执行时,不能调用此函数。
  2. 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
  1. 定义一个事件循环对象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。

发布了24 篇原创文章 · 获赞 0 · 访问量 897

猜你喜欢

转载自blog.csdn.net/HighDS/article/details/103867368