asyncio 的使用姿势

前段时间使用 asyncio 写了一个小程序,摸索出一些使用上的注意事项,记录如下。

本质

有人把 asyncio 这类东西叫做使用同步语法的异步程序,即说明它仍然是异步程序的本质,只不过在语法层面进行了优化,避免陷入回调地狱。取代回调的是 asyncawait 关键字和coroutineFuture 对象。

Future 的意思是未来,即尚未发生之事。coroutine 算是一种自主调度的程序执行模式。对标到回调上,我理解的 Future 就像是回调地狱里的叶子节点,coroutine 算是非叶子节点。因为 coroutine 可以等待 Future,也可以等待其他 coroutine。

await 即 asynchronous wait,是等待事件发生的意思。async 关键字不单独使用,而是作为一个副词修饰其他关键字,比如async def定义一个 coroutine,async for用于异步迭代,async with用于异步进入上下文。

coroutine 和生成器之间有着千丝万缕的联系。因为 Python 中原生能够实现调用后不立即执行功能的就是生成器,所以早期的三方 coroutine 都是基于生成器做的。通过 yield 进行调出,再使用 .send() 调入。Python3.5 以后有了原生的定义和执行 coroutine 的关键字就是 async def 和 await。但基于生成器的语法也被保留,即 @asyncio.coroutineyield from,不过不允许混用。如果有条件使用 3.5 或更新的版本,建议尽量使用新的关键字,因为更加清晰。

由上可见,多层嵌套的回调执行模式本质上仍然保留,只不过不再出现在源代码上,因此便称不上“地狱”了。

time()

当需要使用时间戳的时候,比如 call_at(),需要注意,loop 有一个自己的 loop.time()。他的时间比标准时间戳要小,且会回绕。当用在跟 loop 相关的时间控制时一定要用 loop.time() ,time.time() 只应该用在与 loop 无关的地方,比如业务层或者写日志之类的。

timeout waiter

asyncio 自带的一个超时控制器,不必自己写了,添加注释后的源码如下:

@coroutine
def wait_for(fut, timeout, *, loop=None):
    """Wait for the single Future or coroutine to complete, with timeout.

    Coroutine will be wrapped in Task.

    Returns result of the Future or coroutine.  When a timeout occurs,
    it cancels the task and raises TimeoutError.  To avoid the task
    cancellation, wrap it in shield().

    If the wait is cancelled, the task is also cancelled.

    This function is a coroutine.
    """
    if loop is None:
        loop = events.get_event_loop()

    if timeout is None:
        return (yield from fut)

    waiter = loop.create_future()
    timeout_handle = loop.call_later(timeout, _release_waiter, waiter)  # finish waiter
    cb = functools.partial(_release_waiter, waiter)

    fut = ensure_future(fut, loop=loop)
    fut.add_done_callback(cb)  # 若按时完成,则 finish waiter

    try:
        # wait until the future completes or the timeout
        try:
            yield from waiter  # 等待 waiter finished
        except futures.CancelledError:  # wait_for 被 cancel
            fut.remove_done_callback(cb)
            fut.cancel()
            raise

        if fut.done():  # 若按时完成
            return fut.result()
        else:  # 超时
            fut.remove_done_callback(cb)
            fut.cancel()
            raise futures.TimeoutError()
    finally:
        timeout_handle.cancel()

run_forever()

run_forever() 不接受参数,如果你想跑一个服务类的进程,需要先使用 ensure_future() 之类的添加一个起始 coroutine,并且保证总是有的跑,不会退出。

cancel()

cancel() 一个 coroutine 时,它正在 await 的 coroutine 也会被 cancel。

转载于:https://my.oschina.net/lionets/blog/1542933

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值