python开发-asyncio的使用一

asyncio是python3.4中引入的标准库,它通过事件循环机制实现了对异步的支持。我们使用asyncio时,实际上是先将函数封装成协程放入一个EventLoop(时间循环)中,满足条件时执行相应的协程。

下面主要讲一下asyncio的简单使用和结果获取方式。

一:asyncio的使用
1.asyncio中主要有以下概念:


EventLoop 事件循环:程序开启一个无限的循环,程序员会把一些函数(协程)注册到事件循环上。当满足事件发生的时候,调用相应的协程函数。
coroutine 协程:协程对象,指一个使用async关键字定义的函数,它的调用不会立即执行函数,而是会返回一个协程对象。协程对象需要注册到事件循环,由事件循环调用。
Futures 对象: 代表将来执行或没有执行的任务的结果。它和task上没有本质的区别
task 任务:一个协程对象就是一个原生可以挂起的函数,任务则是对协程进一步封装,其中包含任务的各种状态。Task 对象是 Future 的子类,它将 coroutine 和 Future 联系在一起,将 coroutine 封装成一个 Future 对象
async/await 关键字:python3.5 用于定义协程的关键字,async定义一个协程,await用于挂起阻塞的异步调用接口。其作用在一定程度上类似于yield

2.asyncio中的协程有以下几个状态:


Pending:创建future,还未执行
Running:事件循环正在调用执行任务
Done:任务执行完毕
Cancelled:Task被取消后的状态

3.例子一:使用asyncio.wait()


# coding=utf-8


import time
import asyncio


async def test01(num):
    print(num)
    await asyncio.sleep(2)
    print("--1--")


if __name__ == "__main__":
    start = time.time()
    loop = asyncio.get_event_loop()
    tasks = [test01(i) for i in range(3)]
    loop.run_until_complete(asyncio.wait(tasks))
    print("time:", (time.time() - start))
    loop.close()

打印结果如下:


1
2
0
--1--
--1--
--1--
time: 2.002312421798706

从结果中可以看出,loop中的future是不按顺序执行的,而且3个future执行总共花费2s左右,异步执行省去了很多时间。


asyncio.get_event_loop(): 获取一个EventLoop对象
loop.run_until_complete(): 接收一个Future或Coroutine对象,把这个对象放入loop循环中并运行它,直到运行完成
asyncio.wait(): 传入一个Future对象的集合,等待列表中Future和Coroutine对象执行完成。返回两个集合,分别是已执行完成(Done)与未执行完成(Pending)的Future集合。该方法还可以传入其他参数,具体可以看 https://docs.python.org/3.6/library/asyncio-task.html?highlight=asyncio%20wait#asyncio.wait

4.例子二:使用loop.create_task()


# coding=utf-8


import time
import asyncio


async def test01(num):
    print(num)
    await asyncio.sleep(2)
    print("--1--")


if __name__ == "__main__":
    start = time.time()
    loop = asyncio.get_event_loop()
    tasks = []
    for i in range(3):
        tasks.append(loop.create_task(test01(i)))
    loop.run_until_complete(asyncio.wait(tasks))
    print("time:", (time.time() - start))
    loop.close()

loop.create_task(): 接收一个coroutine对象,返回一个task对象,task是future的子类。

5.例子三:asyncio.ensure_future的使用,把4中的loop.create_task换成asyncio.ensure_future即可


# coding=utf-8


import time
import asyncio


async def test01(num):
    print(num)
    await asyncio.sleep(2)
    print("--1--")


if __name__ == "__main__":
    start = time.time()
    loop = asyncio.get_event_loop()
    # asyncio.BaseEventLoop
    # asyncio.Task
    tasks = []
    for i in range(3):
        tasks.append(asyncio.ensure_future(test01(i)))
    loop.run_until_complete(asyncio.wait(tasks))
    print("time:", (time.time() - start))
    loop.close()

asyncio.ensure_future(): 把coroutine或者awaitable封装成future

6.例子四:asyncio.gather的使用


# coding=utf-8


import time
import asyncio


async def test01(num):
    print(num)
    await asyncio.sleep(2)
    print("--1--")


if __name__ == "__main__":
    start = time.time()
    loop = asyncio.get_event_loop()
    # asyncio.BaseEventLoop
    # asyncio.Task
    tasks = asyncio.gather(
        asyncio.ensure_future(test01(0)),
        asyncio.ensure_future(test01(1)),
        asyncio.ensure_future(test01(2))
    )
    loop.run_until_complete(tasks)

    # 这样也可以, *表示接收的是一个可迭代对象,比如list、tuple
    # tasks = [test01(i) for i in range(3)]
    # loop.run_until_complete(asyncio.gather(*tasks))

    print("time:", (time.time() - start))
    loop.close()

asyncio.gather: 接收多个coroutine或者future,返回一个asyncio.tasks._GatheringFuture对象,注意与asyncio.wait的区别。

二:协程的结果获取
1.协程返回值的获取方式一:


# coding=utf-8


import time
import asyncio


async def test01(num):
    print(num)
    await asyncio.sleep(2)
    print("--1--")
    return num


if __name__ == "__main__":
    start = time.time()
    loop = asyncio.get_event_loop()
    tasks = asyncio.gather(
        asyncio.ensure_future(test01(0)),
        asyncio.ensure_future(test01(1)),
        asyncio.ensure_future(test01(2))
    )
    loop.run_until_complete(tasks)
    for result in tasks.result():
        print("result:", result)
    print("time:", (time.time() - start))
    loop.close()

2.协程返回值的获取方式二:


# coding=utf-8


import time
import asyncio


async def test01(num):
    print(num)
    await asyncio.sleep(2)
    print("--1--")
    return num


if __name__ == "__main__":
    start = time.time()
    loop = asyncio.get_event_loop()
    tasks = list()
    for i in range(3):
        tasks.append(loop.create_task(test01(i)))
    loop.run_until_complete(asyncio.wait(tasks))
    for task in tasks:
        print("result:", task.result())
    print("time:", (time.time() - start))
    loop.close()

1、2中是两种获取协程返回值方式,主要用到了result()方法,该方法是Future的一个方法。1中是先获取全部的结果再遍历得到每个结果,2中是先遍历每个future再获取结果,注意两者区别。

3.协程返回值获取方式三:回调函数


# coding=utf-8


import time
import asyncio


async def test01(num):
    print(num)
    await asyncio.sleep(2)
    print("--1--")
    return num


def callback(future):
    print("callback result:", future.result())


if __name__ == "__main__":
    start = time.time()
    loop = asyncio.get_event_loop()
    tasks = list()
    for i in range(3):
        task = loop.create_task(test01(i))
        task.add_done_callback(callback)
        tasks.append(task)
    loop.run_until_complete(asyncio.wait(tasks))
    print("time:", (time.time() - start))
    loop.close()

上面是使用回调函数的方式获取协程的执行结果。add_done_callback()接收一个函数作为参数,这个函数的最后一个参数必须是当前的future对象。