关于几种容易混淆方法的对比
首先要先知道,协程函数返回的是<class '_asyncio.Task'>,如果使用了asyncio.ensure_future(coro)或者loop.create_task(coro)等方法就变成了<class '_asyncio.Task'>类型,
asyncio.ensure_future(coro)
这个方法现在属于低层级的方法了,< py3.7的版本经常使用,当一个coro或者future通过 asyncio.ensure_future() 的函数被封装进一个 Task时,这个协程 会很快被自动调度执行。
哎呀,说实话这个方法我也不太清楚,感觉在3.7更新asyncio.create_task(coro)后就不怎么用了吧,但是这个新更新的方法要保证是coro才行,而前者coro或者future都可以。
“ 查看 create_task()
函数,它是创建新任务的首选途径 ”这是asyncio文档原话,不过我搞不懂,所以以后不打算常用
asyncio.create_task(coro)
将 coro 协程 打包为一个 Task
放入事件循环准备执行。返回 Task 对象。但是看源码发现如果当前没有正在运行事件循环loop就会报RuntimeError: no running event loop的错误。
源码:
def create_task(coro):
"""Schedule the execution of a coroutine object in a spawn task.
Return a Task object.
"""
loop = events.get_running_loop()
return loop.create_task(coro)
文档连接:https://docs.python.org/zh-cn/3/library/asyncio-task.html#asyncio.create_task
loop.create_task
(coro, *, name=None)
安排一个 协程 放入事件循环准备执行。返回一个 Task
对象。
可以看上一个asyncio.create_task(coro)方法的源码中的返回值就是用了这个方法,可以这样说:如果当前进程存在事件循环loop,则两者是等效的!
关于异步并发
其实前面我们写的爬虫项目就使用了异步asyncio的并发也就是将协程对象逐一放入一个tasks的列表中,创建一个事件循环,最后使用loop.run_until_complete(asyncio.gather(*tasks))
这个其实就是异步的并发了,主要有两个方法asyncio.gather(*task) (task就是协程列表,前面那个*也一定要有)和asyncio.wait(task)。
asyncio.gather 和asyncio.wait的区别
wait
在内部wait()使用一个set保存它创建的Task实例。因为set是无序的所以这也就是我们的任务不是顺序执行的原因。wait的返回值是一个元组,包括两个集合:done和pending,done为已完成的协程Task,pending为超时未完成的协程Task,需通过future.result调用Task的result。wait第二个参数为一个超时值
达到这个超时时间后,未完成的任务状态变为pending,当程序退出时还有任务没有完成此时就会看到如下的错误提示。
gather
不同于wait的是,gather任务无法取消,返回值是已完成Task的result结果列表。并且可以按照传入参数的顺序,顺序输出。
而且gather除了多任务外,还可以对任务进行分组。
import asyncio
import time
from functools import partial
async def get_html(url):
print('start get url', url)
# 必须加await实现协程 这里asyncio.sleep(2)是一个子协程,time.sleep不能可await搭配.
await asyncio.sleep(2)
# time.sleep(2) # 不会报错, 但在协程里不要使用同步的io操作
print('end get url')
return 'cannon'
def callback(url, future):
print(url)
if __name__ == '__main__':
start_time = time.time()
loop = asyncio.get_event_loop() # 开始时间循环
tasks1 = [get_html('http://baidu.com') for i in range(3)]
tasks2 = [get_html('http://google.com') for i in range(3)]
group1 = asyncio.gather(*tasks1) # gather可以进行分组
group2 = asyncio.gather(*tasks2)
loop.run_until_complete(asyncio.gather(group1, group2))
print(time.time() - start_time)
小结
asyncio.wait
比asyncio.gather更低级
。
顾名思义,asyncio.gather
主要集中在收集结果,它等待并按给定的顺序返回其结果。asyncio.wait
只是等待。它不是直接给出结果,而是给出已完成和挂起的任务。
asyncio.gather 和asyncio.wait,推荐使用asyncio.gather。
await关键字深聊
await关键词。异步io的关键在于,await io操作,此时,当前协程就会被挂起,时间循环转而执行其他协程,但是要注意前面这句话,并不是说所有携程里的await都会导致当前携程的挂起,要看await后面跟的是什么,如果跟的是我们定义的协程,则会执行这个协程;如果是asyncio模块制作者定义的固有协程,比如模拟io操作的asyncio.sleep,以及io操作,比如网络io:asyncio.open_connection或者aiomysql.connect()
等这些操作时才会挂起当前协程。
Comments | NOTHING