0
点赞
收藏
分享

微信扫一扫

Python Module — asyncio

圣杰 2022-01-20 阅读 136

目录

文章目录

Python 的协程

Python 对协程的支持经历了多个版本:

  1. Python2.x 对协程的支持比较有限,通过 yield 关键字支持的生成器实现了一部分协程的功能但不完全。
  2. 第三方库 gevent 对协程有更好的支持。
  3. Python3.4 中提供了 asyncio 模块。
  4. Python3.5 中引入了 async/await 关键字。
  5. Python3.6 中 asyncio 模块更加完善和稳定。
  6. Python3.7 中内置了 async/await 关键字。

asyncio

  • Asynchronous I/O(异步 I/O):只发出 I/O 的执行,并不等待 I/O 的结果,释放 CPU,提高程序运行效率。
  • Event loop(事件循环):事件循环是一种处理多并发的有效手段。通过启动一个无限的事件循环,提供事件监测、事件触发等处理工作。可以将 Coroutines 对象注册到事件循环上,当特定的某个事件发生时,就会调用相应的协程函数。
  • Co-routines(协程):通过 async def 声明一个协程函数,对协程函数的调用不会立即执行函数体,而是返回一个 Coroutines 对象。 Coroutines 对象需要注册到事件循环,由事件循环调用。
  • Tasks(任务):是对 Coroutines 对象的进一步封装,包含了面向任务的各种状态,支持任务创建、任务取消等管理功能。在注册事件循环时,通过 run_until_complete() 方法会将 Coroutines 对象包装成为了一个 Task 对象。
  • Futures(将来执行任务的结果):Future 类是 Task 类的父类,实现了 Task 对象执行结果的保存。

在这里插入图片描述

Event Loop

asyncio 编程模型的核心就是一个基于消息的事件循环,程序从 asyncio 模块中获取到一个 Event Loop 的引用,然后把需要执行的协程都扔到 Event Loop 中执行,就实现了异步 I/O。

asyncio 的 Event Loop 拥有多种方式去启动协程,最简单的一种就是使用 run_until_complete() 方法。

import asyncio


async def coroutine():
    print('in coroutine')
    return 'result'

coro = coroutine()

event_loop = asyncio.get_event_loop()
try:    
    print('entering event loop')
    result = event_loop.run_until_complete(coro)
    print(f'it returned: {result}')
finally:
    print('closing event loop')
    event_loop.close()

OUTPUT:

entering event loop
in coroutine
it returned: result
closing event loop

async 与 await

async 关键字用于创建一个协程(返回一个 Coroutines 对象),该 Coroutines 对象需要注册到事件循环,由事件循环调用。针对不同的场景场景有 async def、async for、async with 等几种使方式。

await 关键字用于针对阻塞的 I/O 操作进行挂起,作用与生成器中的 yield(让出)关键字相同,协程函数将会让出控制权。也就是说,当执行协程函数体的过程中遇到了 await 语句,Event Loop 就会把该协程挂起,继而执行其他的协程,直到其他的协程也挂起或者执行完毕后,再执行下一个协程。

async def

async def 用于声明一个协程函数,对协程函数的调用不会立即执行函数体,而是返回一个 Coroutines 对象。一个简单的例子如前文所述。

值的注意的是,async def 支持 “链式协程(Chain coroutines)”,即:父协程可以创建子协程,以此往复,形成一条由若干个协程组成的调用链,并且彼此之间遵循顺序执行。

import asyncio


async def compute(x, y):
    print("Compute %s + %s ..." % (x, y))
    await asyncio.sleep(1.0)
    return x + y

async def print_sum(x, y):
    result = await compute(x, y)
    print("%s + %s = %s" % (x, y, result))

loop = asyncio.get_event_loop()
loop.run_until_complete(print_sum(1, 2))
loop.close()

OUTPUT:

Compute 1 + 2 ...
1 + 2 = 3

在这里插入图片描述

async for

async for 就相当于一个异步的生成器(Generator)。在 Python3.5 之前,实现异步生成器是一件很麻烦的事情,需要定义一个类,并且定义 __iter____next__ 方法,之后才能使用。

现在只需要使用 async for 就能够简单的实现了,而且还支持列表解析等语法。

import asyncio


async def g2():
    yield 1
    yield 2


async def g1():
    async for v in g2():
        print(v)
    return [v * 2 async for v in g2()]


loop = asyncio.get_event_loop()
try:
    result = loop.run_until_complete(g1())
    print(f'Result is {result}')
finally:
    loop.close()

async with

with 是 Python 的 Context Management(上下文管理)语法糖,async with 即是异步的 with。

  • 实现 with 类型
class Sample:

    def __enter__(self):
        print "in __enter__"
        return "Foo"
        
    def __exit__(self, exc_type, exc_val, exc_tb):
        print "in __exit__"


def get_sample():
    return Sample()


with get_sample() as sample:
    print "Sample: ", sample

注意,async with 语法糖同样需要首先实现 async with 类型。目前原生支持 async with 类型的第三方库有比较典型的 aiohttp。

import aiohttp


async def fetch_page(url):
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as response:
            return await response.json()


loop = asyncio.get_event_loop()
result = loop.run_until_complete(fetch_page('http://httpbin.org/get?a=2'))
print(f"Args: {result.get('args')}")
loop.close()

Future 与 Task

正如前文所言,async def 返回的 Coroutines 对象是不能直接运行的,而是在将 Coroutines 对象注册到 Event Loop 之后,由 run_until_complete() 方法将 Coroutines 对象包装成为了一个 Task 对象。

而 Task 类又是 Future 类的子类,Future 类实现了保存 Task 对象运行后的状态,Future 对象可以用于在未来获取协程的执行结果。

当有需要时,我们可以显式地创建了 task 对象。并且,在 task 对象加入 Event Loop 之前的状态为 Pending,执行当完成后的状态为 Finished。

import asyncio
import time


now = lambda: time.time()


async def do_some_work(x):
    print("waiting:", x)

start = now()
coroutine = do_some_work(2)
loop = asyncio.get_event_loop()

task = loop.create_task(coroutine)
print(task)

loop.run_until_complete(task)
print(task)

print("Time:", now()-start)

OUTPUT:

<Task pending coro=<do_some_work() running at test_asyncio.py:8>>
waiting: 2
<Task finished coro=<do_some_work() done, defined at test_asyncio.py:8> result=None>
Time: 0.0010194778442382812

另外,除了 loop.create_task() 方法之外,也可以使用 asyncio.ensure_future(coroutine) 方法来将一个 Coroutines 对象封装成一个 Task 对象,但两者有着不同的使用场景。后者更多的是用于确保可以得到相应的 Future Result。

  • loop.create_task
AbstractEventLoop.create_task(coro)
Schedule the execution of a coroutine object: wrap it in a future. Return a Task object.

Third-party event loops can use their own subclass of Task for interoperability. In this case, the result type is a subclass of Task.

This method was added in Python 3.4.2. Use the async() function to support also older Python versions.
  • asyncio.ensure_future
asyncio.ensure_future(coro_or_future, *, loop=None)
Schedule the execution of a coroutine object: wrap it in a future. Return a Task object.

If the argument is a Future, it is returned directly.
举报

相关推荐

0 条评论