关于几种容易混淆方法的对比

首先要先知道,协程函数返回的是<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文档原话,不过我搞不懂,所以以后不打算常用

文档链接:https://docs.python.org/zh-cn/3/library/asyncio-future.html?highlight=ensure_future#asyncio.ensure_future

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.waitasyncio.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()等这些操作时才会挂起当前协程


所念皆星河