python-任务和协程文档翻译及延伸

1. 协程概述

  • 能被asyncio调用的协程可以通过两种方式实现:

    1. 使用async def语句
    2. 使用生成器。

    第一种方式在Python3.5添加,在没有向下兼容的考虑时推荐使用。
    基于生成器的协程应该被@asyncio.coroutine
    装饰,尽管这不是严格规定(not strictly enforced)。该装饰器能够兼容async def
    定义的协程。基于生成器的协程使用 PEP 380引入的yield from语法,而不是原始的yield语法。

  • 协程一词正如生成器一词,被用于两个不同但相关的概念:

    1. 定义一个协程的函数。如果要消除歧义,我们称之为协程函数 (iscoroutinefunction()
      returns True)。
    2. 调用一个协程函数返回的对象。这个对象代表的运算和I/O操作最终将被完成。如果要消除歧义,我们称之为协程对象(iscoroutine()
      returns True)。
  • 协程能做的事情:

  1. result = await future或者result = yield from future
    暂停协程直到future被完成,然后返回future的结果,或者抛出一个异常,该异常将被传播。(如果future被取消,将抛出一个CancelledError异常。)要注意的是,tasks就是futures,一切关于futures的也适用于tasks
  2. result = await coroutine或者result = yield from coroutine
    等待另一个协程产出结果(或者抛出一个会被传播的异常)。协程表达式必须能被另一个协程调用。
  3. return expression
    产生结果给一个使用await
    或yield from来等待该协程
    的另一个协程
  4. raise exception
    抛出异常一个使用await
    或yield from来等待该协程
    的另一个协程
  • 调用一个协程并不启动它的代码运行——调用返回的协程对象什么也不做,直到你安排它执行。有两种基本的方式使它开始运行:
  1. 在另一个运行的协程中调用await coroutine或者yield from coroutine
  2. 使用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()
python-任务和协程文档翻译及延伸_第1张图片
执行流程图
  • 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框架

你可能感兴趣的:(python-任务和协程文档翻译及延伸)