1. 协程概述
-
能被asyncio调用的协程可以通过两种方式实现:
- 使用
async def
语句 - 使用生成器。
第一种方式在Python3.5添加,在没有向下兼容的考虑时推荐使用。
基于生成器的协程应该被@asyncio.coroutine
装饰,尽管这不是严格规定(not strictly enforced)。该装饰器能够兼容async def
定义的协程。基于生成器的协程使用 PEP 380引入的yield from
语法,而不是原始的yield
语法。 - 使用
-
协程一词正如生成器一词,被用于两个不同但相关的概念:
- 定义一个协程的函数。如果要消除歧义,我们称之为协程函数 (iscoroutinefunction()
returns True)。 - 调用一个协程函数返回的对象。这个对象代表的运算和I/O操作最终将被完成。如果要消除歧义,我们称之为协程对象(iscoroutine()
returns True)。
- 定义一个协程的函数。如果要消除歧义,我们称之为协程函数 (iscoroutinefunction()
协程能做的事情:
-
result = await future
或者result = yield from future
暂停协程直到future
被完成,然后返回future
的结果,或者抛出一个异常,该异常将被传播。(如果future
被取消,将抛出一个CancelledError异常。)要注意的是,tasks
就是futures
,一切关于futures
的也适用于tasks
。 -
result = await coroutine
或者result = yield from coroutine
等待另一个协程产出结果(或者抛出一个会被传播的异常)。协程表达式必须能被另一个协程调用。 -
return expression
产生结果给一个使用await
或yield from来等待该协程的另一个协程 -
raise exception
抛出异常一个使用await
或yield from来等待该协程的另一个协程
- 调用一个协程并不启动它的代码运行——调用返回的协程对象什么也不做,直到你安排它执行。有两种基本的方式使它开始运行:
- 在另一个运行的协程中调用
await coroutine
或者yield from coroutine
- 使用ensure_future()
函数或者AbstractEventLoop.create_task()
方法来安排它的执行。
- 协程(以及任务)只能在事件循环运行的时候执行。
2. @asyncio.coroutine
- 它是一个标记基于生成器的协程的装饰器。它使得生成器使用
yield from
调用async def
定义的协程,并且使该生成器能够被async def
定义的协程调用(比如使用await
语句调用)。 - 没有必要去装饰用
async def
定义的协程它们自己。 - 如果一个生成器在销毁前没有被
yield from
,一个错误信息将被日志记录。参看Detect coroutines never scheduled。
3. 最简单的hello world 协程
import asyncio
async def hello_world():
print("Hello World!")
loop = asyncio.get_event_loop()
# 阻塞式调用,直到hello_world()协程结束时返回
loop.run_until_complete(hello_world())
# run_until_complete只接受A Future, a coroutine or an awaitable
loop.close()
- 相关:在Base Event Loop文档中的Hello World with call_soon():
使用AbstractEventLoop.call_soon()
方法来安排一个回调。该回调打印“Hello World”然后停止事件循环。
import asyncio
#
def hello_world(loop):
print('Hello World')
loop.stop()
#
loop = asyncio.get_event_loop()
#
# 安排一个hello_world()的调用
loop.call_soon(hello_world, loop)
#
# 将被loop.stop()打断的阻塞式调用
loop.run_forever()
loop.close()
4. 打印当前时间的协程
- 在5秒时间内每秒打印当前时间,利用了sleep()
函数
import asyncio
import datetime
async def display_date(loop):
end_time = loop.time() + 5.0
while True:
print(datetime.datetime.now())
if (loop.time() + 1.0) >= end_time:
break
await asyncio.sleep(1)
# asyncio.sleep睡眠指定时间再返回的协程,asyncio中有相应的调度机制
loop = asyncio.get_event_loop()
loop.run_until_complete(display_date(loop))
loop.close()
打印结果
5. 链式协程
-
compute()
协程被print_sum()
等待:当compute()
被执行完成并返回结果时print_sum()
继续执行
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()
执行流程图
-
Task被 AbstractEventLoop.run_until_complete()
方法创建,该方法在接受一个协程对象而不是一个任务时会创建Task。 - 该图只是展示控制流,并没有精确地描述流程内部。比如,
sleep
协程创建了一个内部的future使用 AbstractEventLoop.call_later()
来在一秒后唤醒任务。
6. async/await 与asyncio
- David Beazley指出async/await 实际上是异步编程的 API ,人们不应该将async/await等同于asyncio,而应该将asyncio看作是一个利用async/await API 进行异步编程的框架。
- Python 3.5 协程究竟是个啥
7. 协程asyncio相关的优秀应用
- asyncpg -- A fast PostgreSQL Database Client Library for Python/asyncio 一个快速的PostgreSQL数据库的客户端库,3倍于psycopg2。
- uvloop -- uvloop is a fast, drop-in replacement of the built-in asyncio event loop. uvloop is implemented in Cython and uses libuv under the hood. 内置事件循环器的快速、插入式替代品。
- aiohttp -- http client/server for asyncio 异步http框架