1.协程的意义
在一个线程中,如果遇到IO等待,线程不会一直在等待,而是利用空余时间去完成别的任务(充分利用线程)。
示例:下载图片
①普通的方式下载图片(同步方式)
②使用协程的方式下载图片(异步方式,也可通过线程池、进程池完成,可称为异步编程)
2.基于协程方式的异步编程
2.1 事件循环
可以理解为一个死循环,去检查并执行某些代码。io等待被认为是不可执行的任务,如果碰到不可执行的任务,会将它放置在哪里,去执行下一个可执行任务。如果io请求执行完,该任务就变成可执行任务。
代码实现:
2.2 快速上手
协程函数:定义 函数时 async def 函数名;
协程对象:执行 协程函数() 得到协程对象。
!!!当一个 协程函数()只是得到了一个协程对象,他的内部函数是不会执行的。
只有当协程函数、协程对象和事件循环搭配使用,才能执行协程函数内部代码(需要将协程对象交给事件循环进行处理)。
python3.7后有更简单的写法:
2.3 await关键字
await 后一般跟 可等待对象 (协程对象,Future, Task对象-->io等待)等这个io(等待的这个过程中会切换到其他任务),当io结束后才会去执行
示例一:
示例二:
示例三:
一个协程函数中可以有多个await,遇到await后可以切换到其他协程函数中,但是不会顺序执行等待函数所在代码!!!(await就是等待对象的值得到结果之后,再去继续向下走)
2.4 Task对象
在事件循环中可以并发的添加多个任务。
Task是用于并发调度协程,通过asyncio.create_task(协程对象)的方式创建Task对象,可以让协程函数加入事件循环中等待被调度执行。处理使用asyncio.create_task(协程对象)函数以外,还可以使用低层级的loop.create_task()或ensure_future()函数,不建议手动实例化Task对象。
asyncio.create_task(协程对象)在python3.7后才被引入,在python3.7之前可以使用低级的asyncio.ensure_future()函数。
以上将Task任务放到事件循环中,不是常用的方法。(在执行main代码时,事件循环已经创建,在asyncio.run()执行时)
示例:(这种方法使用场景较少)
Task对象的作用:可以将某个任务立即加入到事件循环中,并发的创建多个任务。
2.5 asyncio的.Future对象
一般不会直接使用,是Task的基类。Future内部封装了_state值,他维护await等待的 状态,如果它已经完成,await就不会再这等待,可以继续往下走。
示例一:
Future对象什么都没有做,所以他没有结果会一直等待下去。
示例二:
2.6 concurrent.futures.Future对象
使用线程池、进程池实现异步操作时用到的对象。
pool.submit()让你的线程池,拿一个线程,执行函数(往线程池提交任务申请)。
写代码的过程中,可能会存在交叉使用的情况。一般情况下协程和线程、进程池不会交叉使用。但是某个项目80%都是基于协程异步编程+MysQL(不支持,得将两种future结合,用线程和进程做异步编程)