协程的概念
协程(英文叫作 Coroutine),是一种用户态的轻量级线程。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈。线程的执行完全是操作系统控制的,而协程的控制权在于用户,本质是为了能让多组过程能不独自占用完所有资源,在一个线程内交叉执行,达到高并发的目的。
协程的优点
- 极高的执行效率,因为无需系统内核的上下文切换,减小开销;
- 无需原子操作锁定及同步的开销,不用担心资源共享的问题;
协程的用法
Python 中实现协程的库是asyncio ,通过async def来定义一个协程函数,通过await来执行一个协程对象。
- event_loop事件循环:程序开启一个无限循环,把一些函数注册到这个事件循环上,当满足条件发生的时候,就会调用相应的协程函数。
- coroutine 协程:协程对象,指一个使用async关键字定义的函数,它的调用不会立即执行函数,而是会返回一个协程对象。协程对象需要注册到事件循环,由事件循环调用。
- task任务:它是对协程对象的进一步封装,包含了任务的各个状态。
- future:代表将来执行或没有执行的任务,和 task 没有本质区别。
- async/await :python用于定义协程的关键字,async 定义一个协程,await 用来挂起阻塞方法的执行。
- 可等待对象 :被关键字 await 标记就表明它是个可等待对象 ,Python 协程属于可等待对象,因此可以在其他协程中被等待。换言而之,一个异步函数不能直接调用另一个异步函数,必须在被调用的异步函数前添加 await关键字。
- 协程函数与协程对象:协程函数,定义为 async def 的函数;协程对象,通过调用协程函数所返回的对象。
我们只要通过async定义协程,await定义阻塞,然后封装成future的task,放入循环的事件列表中,就等着返回数据。
协程的示例
1. 定义协程函数:
async def do_some_work(x):
print("waiting:",x)
# await挂起阻塞, 相当于yield, 通常是耗时操作
await asyncio.sleep(x)
return "Done after {}s".format(x)
2.定义回调函数:
# 回调函数,它的唯一参数只能是future
def callback(future):
print("callback:",future.result())
3.将 coroutine 对象转化为 task 对象:
tasks = []
for i in range(1, 4):
# 定义多个协程,同时预激活
coroutine = do_some_work(i)
task = asyncio.ensure_future(coroutine)
# 添加回调函数
task.add_done_callback(callback)
tasks.append(task)
4.获取事件循环:
loop = asyncio.get_event_loop()
5.异步执行协程:
loop.run_until_complete(asyncio.wait(tasks))
6.关闭事件循环:
loop.close()
完整代码示例:
import time
import asyncio
# async定义协程
async def do_some_work(x):
print("waiting:",x)
# await挂起阻塞, 相当于yield, 通常是耗时操作
await asyncio.sleep(x)
return "Done after {}s".format(x)
# 回调函数,和yield产出类似功能
def callback(future):
print("callback:",future.result())
tasks = []
for i in range(1, 4):
# 定义多个协程,同时预激活
coroutine = do_some_work(i)
task = asyncio.ensure_future(coroutine)
task.add_done_callback(callback)
tasks.append(task)
# 获取事件循环,把任务协程放在里面,
loop = asyncio.get_event_loop()
try:
# 异步执行协程,直到所有操作都完成
loop.run_until_complete(asyncio.wait(tasks))
for task in tasks:
print("Task ret:",task.result())
except KeyboardInterrupt as e: # 协程任务的状态控制
print(asyncio.Task.all_tasks())
for task in asyncio.Task.all_tasks():
print(task.cancel())
loop.stop()
loop.run_forever()
finally:
loop.close()
协程技巧总结
一、运行协程的两种方法:
1. asyncio.wait( )
loop.run_until_complete(asyncio.wait(tasks))
2. asyncio.gather( )
loop.run_until_complete(asyncio.gather(*tasks))
二、创建 task 的两种方法:
1.需要提前声明 loop
# 用 get_event_loop 方法创建事件循环 loop
loop = asyncio.get_event_loop()
# 调用 loop 对象的 create_task 方法
task = loop.create_task(coroutine)
2.不需要提前声明 loop
# 调用 asyncio 的 ensure_future 方法
task = asyncio.ensure_future(coroutine)
三、获取结果的两种方法:
1. 绑定回调函数
# 创建一个任务协程
task = asyncio.ensure_future(coroutine)
# 绑定回调函数,callback 是一个普通函数
task.add_done_callback(callback)
2. 在 task 运行完后调用 result 方法
for task in tasks:
print("Task ret:",task.result())