异步IO——协程与任务

link:

高层级API

协程

import asyncio
import time

async def say_after(delay, what):
    await asyncio.sleep(delay)
    print(what)

async def main():
    task1 = asyncio.create_task(
        say_after(1, 'hello'))

    task2 = asyncio.create_task(
        say_after(2, 'world'))

    print(f"started at {time.strftime('%X')}")

    # Wait until both tasks are completed (should take
    # around 2 seconds.)
    await task1
    await task2

    print(f"finished at {time.strftime('%X')}")

asyncio.run(main())
  1. async定义一个协程
  2. await等待一个协程
  3. asyncio.create_task创建一个task以便后续并发执行作为任务的多个协程
  4. asyncio.run运行一个协程

可等待对象

  • 可以在await中使用的对象,主要有三种类型协程taskfuture
    • 协程函数: async def定义的函数, 协程对象: 调用协程函数所返回的对象
    • 支持旧的基于生成器的协程
    • 任务: asyncio.create_task(), 可以将一个协程函数包装成一个任务,该协程将自动排入日程准备立即运行
    • future是一个低层级可等待对象,表示一个异步操作的最终结果, e.g. asyncio.gather() loop.run_in_executor()
    • TaskFuture子类
# 基于生成器的协程
@asyncio.coroutine
def old_style_coroutine():
    yield from asyncio.sleep(1)

常用高层级API

运行

  • asyncio.run(coro, *, debug=False)

创建任务

  • asyncio.create_task(coro, *, name=None)
    • 支持3.7+, asyncio.ensure_future支持所有python版本
    • coro协程打包为一个Task排入日程准备执行,即碰到await之后即有机会运行
    • 该任务会在get_running_loop()返回的事件循环中执行,如果当前线程没有在运行的循环则会引发 RuntimeError

等待

  • coroutine asyncio.sleep(delay, result=None, *, loop=None)
    • 阻塞 delay 指定的秒数, 如果指定了result,则当协程完成时将其返回给调用者, sleep()总是会挂起当前任务,以允许其他任务运行

并发运行

  • awaitable asyncio.gather(*aws, loop=None, return_exceptions=False)

    • 并发运行aws序列中的可等待对象
    • 如果aws中某个可等待对象为协程,将自动作为一个任务加入日程
    • 如果所有可等待对象都成功完成,结果将返回一个和aws中顺序一致的列表
    • 如果return_exceptionsFalse,在遇到第一个异常时, gather将被标记为已完成,在此情况下,aws序列中其他的对象将不可以被取消
    • 如果return_exceptionsTrue, 异常会和成功的结果一起返回
    • 如果aws中任意一个被取消,则等价于CancelledError异常,gather不会被取消
  • coroutine asyncio.wait(aws, *, loop=None, timeout=None, return_when=ALL_COMPLETED)

    • 并发运行aws序列中的可等待对象,并阻塞线程直到return_when
    • 用法: done, pending = await asyncio.wait(aws),返回两个Future集合
    • return_when的取值包括: FIRST_COMPLETED, FIRST_EXCEPTION, ALL_COMPLETED
    • wait()会自动将协程作为任务加入日程,以后将以 (done, pending) 集合形式返回显式创建的任务对象
    • 注意对比wait()gather()方法
  • asyncio.as_completed(aws, *, loop=None, timeout=None)

    • 并发运行aws中可等待对象,返回一个协程迭代器。
注意 wait
async def foo():
    return 42

coro = foo()
done, pending = await asyncio.wait({coro})

if coro in done:
    # This branch will never be run!

task = asyncio.create_task(foo())
done, pending = await asyncio.wait({task})

if task in done:
    # Everything will work as expected now.
三种方式样例
async def phase(i):
    print('in phase {}'.format(i))
    await asyncio.sleep(0.5 - (0.1 * i))
    print('done with phase {}'.format(i))
    return 'phase {} result'.format(i)


async def main_as_completed(num_phases):
    print('starting main_as_completed')
    phases = [
        phase(i)
        for i in range(num_phases)
    ]
    print('waiting for phases to complete')
    results = []
    for next_to_complete in asyncio.as_completed(phases):
        answer = await next_to_complete
        print('received answer {!r}'.format(answer))
        results.append(answer)
    print('results: {!r}'.format(results))
    return results


async def main_wait(num_phases):
    print('starting main_wait')
    phases = [
        phase(i)
        for i in range(num_phases)
    ]
    print('waiting for phases to complete')
    done, pending = await asyncio.wait(phases)
    results = [t.result() for t in done]
    print('results: {!r}'.format(results))
    return results


async def main_gather(num_phases):
    print('starting main_gather')
    phases = [
        phase(i)
        for i in range(num_phases)
    ]
    print('waiting for phases to complete')
    results = await asyncio.gather(*phases)
    print('results: {!r}'.format(results))
    return results

print('### main_as_completed ###')
asyncio.run(main_as_completed(3))
print('### main_wait ###')
asyncio.run(main_wait(3))
print('### main_gather ###')
asyncio.run(main_gather(3))

"""
### main_as_completed ###
starting main_as_completed
waiting for phases to complete
in phase 2
in phase 0
in phase 1
done with phase 2
received answer 'phase 2 result'
done with phase 1
received answer 'phase 1 result'
done with phase 0
received answer 'phase 0 result'
results: ['phase 2 result', 'phase 1 result', 'phase 0 result']
### main_wait ###
starting main_wait
waiting for phases to complete
in phase 2
in phase 1
in phase 0
done with phase 2
done with phase 1
done with phase 0
results: ['phase 1 result', 'phase 0 result', 'phase 2 result']
### main_gather ###
starting main_gather
waiting for phases to complete
in phase 0
in phase 1
in phase 2
done with phase 2
done with phase 1
done with phase 0
results: ['phase 0 result', 'phase 1 result', 'phase 2 result']
"""
  • 注意这三种方法的调用方式
  • gather是顺序的,可以返回异常信息,等待所有结果
  • as_completed返回一个迭代器,不保证顺序,不等带返回结果
  • wait返回一个元组,不保证顺序,等待返回结果

屏蔽取消

  • awaitable asyncio.shield(aw, *, loop=None)
    • 保护一个可等待对象,防止被取消
    • res = await shield(something())something的角度来看,取消操作并没有发生,但是调用者已被取消,所以await还是会引发CancelledError

超时

  • coroutine asyncio.wait_for(aw, timeout, *, loop=None)
    • 等待一个awaitable对象返回,并且在timeout秒之后超时

线程安全

  • asyncio.run_coroutine_threadsafe(coro, loop)
    • 向指定事件循环提交一个协程

内省

  • asyncio.current_task(loop=None)
    • 返回当前运行的task实例,如果没有,则返回None
  • asyncio.all_tasks(loop=None)
    • 返回事件循环所运行的未完成的task对象的集合。

Task对象

  • class asyncio.Task(coro, *, loop=None, name=None)
    • Taskfuture的子类,用来在事件循环中运行协程。如果一个协程在等待一个Future对象,Task对象会挂起该协程的执行并等待该Future对象完成。
    • 可使用高层级的asyncio.create_task()创建,也可使用低层级的loop.create_task() 或 ensure_future()来创建
    • Task 对象支持 contextvars 模块。当一个 Task 对象被创建,它将复制当前上下文,然后在复制的上下文中运行其协程。
  • cancel()
    • 取消一个正在运行的task,抛出一个CancelledError异常,如果取消期间一个协程正在等待一个future对象,改future对象也将被取消
  • cancelled()
    • task是否被取消
  • done()
    • task是否完成
  • result()
    • task的结果
  • exception()
    • 返回一个task对象的异常

源码片段

def create_task(coro):
    """Schedule the execution of a coroutine object in a spawn task.

    Return a Task object.
    """
    loop = events.get_running_loop()
    return loop.create_task(coro)

def create_task(self, coro):
    """Schedule a coroutine object.

    Return a task object.
    """
    self._check_closed()
    if self._task_factory is None:
        task = tasks.Task(coro, loop=self)
        if task._source_traceback:
            del task._source_traceback[-1]
    else:
        task = self._task_factory(self, coro)
    return task
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值