首页 > 编程笔记 > Python笔记 阅读:4

Python asyncio的用法(附带实例)

Python 3.4 引入了协程的概念,加入了 asyncio,以生成器对象为基础。Python3.5 又提供了 async/await 语法层面的支持,本节将基于 Python3.5 了解异步编程的概念及 asyncio 的用法。

在开始介绍之前,需要了解以下几个概念:

Python asyncio用法实例

Python3.4 首先使用 asyncio 提供的 @asyncio.coroutine 把一个 generator(生成器)标记为 coroutine(协程)类型,然后在 coroutine 内部用 yield from 调用另一个 coroutine 实现异步操作。

为了简化并更好地标识异步 IO,从 Python 3.5 开始就引入新的语法 async 和 await,让 coroutine 的代码更简洁易读。

解决 Python3.4 和 Python3.5 的差异只需要进行如下关键字的替换:
@asyncio.coroutine (Python3.4)<==>async (Python3.5);
yield from (Python3.4)<==>await (Python3.5).

使用 asyncio 编写程序代码为:
import asyncio
import time

now = lambda: time.time()
async def func(x):
    print('Waiting for %d s' % x)
    await asyncio.sleep(x)
    return 'Done after {} s'.format(x)

start = now()

coro1 = func(1)
coro2 = func(2)
coro3 = func(4)

tasks = [
    asyncio.ensure_future(coro1),
    asyncio.ensure_future(coro2),
    asyncio.ensure_future(coro3)
]

loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))

for task in tasks:
    print("Task return: ", task.result())

print('Program consumes: %f s' % (now() - start))
分析程序代码如下:
运行结果为:
Waiting for 1 s
Waiting for 2 s

Waiting for 4 s
Task return: Done after 1s
Task return: Done after 2s
Task return: Done after 4s
Program consumes: 4.005412 s
程序的总运行时间比 4 秒多一点,如果是同步模型,那么执行时间至少为 7 秒。

下图分别展示了单线程、多线程、协程运行图。随着时间的推移,三种模式分别有 3 个任务需要完成,每个任务都在等待 IO 操作时阻塞自身,IO 阻塞时间用灰色表示。


图 1 单线程、多线程、协程运行图

在单线程同步模型中,任务按照顺序执行。如果某个任务因为 IO 操作而阻塞,则其他所有的任务都必须等待,直到 IO 操作完成之后才能依次执行。在 IO 操作时,CPU 除了等待什么事也不能干,非常浪费 CPU 资源。

在多线程模型中,3 个任务分别在独立的线程中执行。这些线程由操作系统管理,在多核 CPU 上可以并行执行,在单核 CPU 上可以交错执行。当某个线程遇到 IO 操作阻塞时,其他线程可以继续执行。与同步模型相比,虽然这种方式更有效率,但开发者必须写代码保护共享资源。

在使用协程的异步 IO 模型中,3 个任务交错执行,处于同一个线程中,当其中一个协程遇到 IO 操作时,则跳转到其他协程继续执行,既没有浪费 CPU 资源,也不需要加锁等安全机制。

相关文章