文章目录
异步1、协程定义(微线程)
2、实现协程2.1 asyncio定义执行方法执行顺序
2.2 async&await关键字
3、意义总结案例
4. 异步编程4.1 事件循环4.2 运行协程4.3 await案例1案例2
4.4 Task对象案例1案例2
4.5 Future对象4.6 异步上下文管理器4.7 uvloop
5. 实战案例爬虫
异步
1、协程
协程不是计算机提供的,是人为创造的。
定义(微线程)
用一个线程在代码之间切换执行(是一种用户态内的上下文切换技术)
def fun1():
print(1)
...
print(2)
def fun2():
print(3)
...
print(4)
func1
()
func2
()
正常情况
顺序执行执行完func1后再执行func2 异步
一个线程可以实现在代码间来回切换比如可能先输出1,再输出3,返回去再输出2可以人为的控制
2、实现协程
greenlet:早期模块yield:生成器asyncio装饰器async、await关键词(主流:python3.5)
2.1 asyncio
@asyncio
.coroutine
def func1():
print(1)
yield from asyncio
.sleep
(2)
print(2)
@asyncio
.coroutine
def func2():
print(3)
yield from asyncio
.sleep
(2)
print(4)
定义
一个普通函数加上装饰器(@asyncio.coroutine)后就是定义为协程函数
coroutine:协程的意思
执行方法
与普通函数直接调用执行不同协程函数的执行方法
定义一个looploop = asyncio.get_event_loop()然后把异步函数扔进去里面loop.run_until_complete(异步函数)多个异步函数时,可以定义一个tasks
将两个异步函数放在tasks列表里loop中传入tasks即可
tasks
= [
asyncio
.ensure_future
(func1
()),
asyncio
.ensure_future
(func2
()),
]
loop
= asyncio
.get_event_loop
()
loop
.run_until_complete
(asyncio
.wait
(tasks
))
执行顺序
只有一个线程执行(随机先执行某个异步函数)假设先执行func1()当一个线程在执行协程内容的时候,遇到yield,就会等待X秒但在等待的过程中,就会切换到其他的协程函数去执行在func2()中也遇到asyncio.sleep()的话,就两个异步函数都会进入等待状态只有等到某一个异步函数的asyncio.sleep过去后,才会继续执行输出2,func1()执行完后再继续执行func2(),输出4
2.2 async&await关键字
async def func1():
print(1)
await asyncio
.sleep
(2)
print(2)
async def func2():
print(3)
await asyncio
.sleep
(2)
print(4)
与asyncio的差别:就是代码更加简洁
3、意义
在一个线程中如果遇到IO等待时间,线程不会傻傻等,利用空闲的时间再去干点其他事,不让线程闲着
总结
遇到IO阻塞自动切换
网络IO请求:下载图片、访问网站适用于爬虫,就是在下载或者访问的等待过程(等待下载好、网站给我们的响应)可以执行其他异步函数,让他们也都发送请求,不等网站传回给我们响应,几秒后下载完成或者访问成功再返回去执行正常流程:(同步)
发送请求,等待2秒后,下载完成,保存执行下一个,发送请求,也是等待2秒 协程流程:(异步)
发送请求,需要等待2秒马上切换到另一个函数发送请求2秒后,第一个异步函数下载完成,保存,同时,第二个异步函数也下载完成 耗时比较(大概)
正常流程:2S+2S异步:2S反正就是快很多,如果函数更多,io等待时间长,那异步的优势就更大了
案例
4. 异步编程
4.1 事件循环
理解成为一个死循环,去检测并执行某些代码
任务列表
= [任务
1,任务
2,任务
3...]
while True:
可执行的任务列表,已完成的任务列表
= 去任务列表中检查所有的任务,将’可执行‘和’已完成‘的任务返回
for 就绪任务
in 可执行的任务列表:
执行已就绪的任务
for 已完成的任务
in 已完成的任务列表:
在任务列表中移除已完成的任务
直到任务列表中的所有任务都完成了,即没有任务了,就终止循环
生成
import asyncio
loop
= asyncio
.get_event_loop
()
loop
.run_until_complete
(任务列表
)
4.2 运行协程
协程函数:定义函数 async def 函数名()协程对象:执行协程函数()得到的协程对象
async def func():
pass
result
= func
()
注意:执行协程函数创建协程对象,该函数内部代码不会执行。
运行协程函数
将协程函数、协程对象以及事件循环搭配起来,才能运行就是将协程对象交给事件循环来处理
async def func():
print('运行起来')
result
= func
()
asyncio
.run
(协程对象
)
4.3 await
await + 可等待的对象(协程对象、Future、Task对象)----> IO等待
案例1
import asyncio
async def func():
print('开始')
response
= await asyncio
.sleep
(2)
print('结束')
asyncio
.run
(func
())
在整个过程中线程不会去等待它的睡眠,而是一直在死循环遍历任务列表,当协程对象的状态从不可执行变为可执行时才会执行下面的代码
案例2
import asyncio
async def others():
print('开始')
await asyncio
.sleep
(2)
print('结束')
return '返回值'
async def func():
print('最先执行')
response
= await others
()
print response
asyncio
.run
(func
())
整个过程就是先输出:最先执行,然后输出:开始,等待2S后,输出结束,最后输出返回值
4.4 Task对象
在循环事件中添加多个任务
任务列表 = 【任务1,任务2,任务3…】Tasks 用于并发调度协程
就是说任务列表中有很多个任务当有任务遇到IO等待时,就切换执行其他的任务 创建Tasks
通过asyncio.create_task(协程对象)的方式创建Task对象 执行Tasks
await asyncio.wait(tasks)asyncio.wait 源码内部会对列表中的每个协程执行ensure_future从而封装为Task对象,所以在和wait配合使用时task_list的值为[func(),func()] 也是可以的。
案例1
import asyncio
async def func():
print(1)
await asyncio
.sleep
(2)
print(2)
return "返回值"
async def main():
print("main开始")
task_list
= [
asyncio
.create_task
(func
(), name
="n1"),
asyncio
.create_task
(func
(), name
="n2")
]
print("main结束")
done
, pending
= await asyncio
.wait
(task_list
, timeout
=None)
print(done
, pending
)
asyncio
.run
(main
())
案例2
import asyncio
async def func():
print("执行协程函数内部代码")
response
= await asyncio
.sleep
(2)
print("IO请求结束,结果为:", response
)
coroutine_list
= [func
(), func
()]
done
,pending
= asyncio
.run
( asyncio
.wait
(coroutine_list
) )
4.5 Future对象
不常用,更底层,是Task的基类
4.6 异步上下文管理器
pass
4.7 uvloop
pip install uvloop
import asyncio
import uvloop
asyncio
.set_event_loop_policy
(uvloop
.EventLoopPolicy
())
asyncio
.run
(....)
5. 实战案例
爬虫
在这里插入代码片