• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
孙龙 程序员
少时总觉为人易,华年方知立业难
博客园    首页    新随笔    联系   管理    订阅  订阅
Future与Task

 

Tasks and coroutines

翻译的python官方文档

这个问题的恶心之处在于,如果你要理解coroutine,你应该理解future和task。而你如果想理解future和task你应该先理解coroutine。所以在第一遍阅读官方文档的时候,感觉完全是在梦游。但读到第二遍和第三遍的时候,就清楚很多了。

Coroutines

协程(coroutine)包括两个概念:

  1. 协程函数(async def 或者 @asyncio.coroutine)
  2. 协程函数所返回的协程对象。

协程功能:

  1. 通过result = await future或者 result = yeild from future,悬挂协程,直到future完成,获取future的结果/异常(参见下面对future及future结果的描述,或等看完future之后回来再阅读这一段)。
  2. 通过 result = await coroutine 或者 result = yeild from coroutine 等待另一个协程的结果(或者异常,异常会被传播)。
  3. returen expression 返回该协程的结果,被await,或者yield from获取。
  4. raise exception,抛出异常,被await,或者yield from获取。

调用协程函数并不能使该协程运行。调用协程函数所返回的协程对象,在被你安排执行之前,不会做任何事情。有两种方式可以启动它:

  1. 通过在一个已经启动的协程中调用:await coroutine或者yield from coroutine
  2. 或者通过ensure_task()以及loop.create_task()安排协程的执行。

只有事件循环在运行的时候,协程才能运行

在本文档中,有些普通函数返回了一个future,也被标记为coroutine。这是故意的,这样以后就可以自由使用这些函数。如果是在回调代码中使用这个函数,用ensure_future包装他。

hello_world.py

import asyncio

#  创建一个协程
async def hello_world():
    print("Hello World!")

loop = asyncio.get_event_loop()
# Blocking call which returns when the hello_world() coroutine is done
# 在事件循环中调用这个协程
# 不过这里只有一个协程,而其不阻塞
loop.run_until_complete(hello_world())
loop.close()

hello_world2.py

# 这段代码和上面的代码执行结果是相同的。只不过用了另一种调用协程的方式
# 先在loop.call_soon()中安排好,再通过loop.run_forever()调用
# 注意,这里在hello_world中,调用了loop.stop(),否则事件循环就不会终止。
import asyncio

def hello_world(loop):
    print('Hello World')
    loop.stop()

loop = asyncio.get_event_loop()

# Schedule a call to hello_world()
loop.call_soon(hello_world, loop)

# Blocking call interrupted by loop.stop()
loop.run_forever()
loop.close()

 

 注意这里return 1+2,实际上是raise StopIteration(3)协程其实是在不停返回结果的。最后的结果才会被返回。

future

future是一个容器,或者占位符(placeholder),用于接受异步的结果。这里指的是asyncio.Future而不是coroutines.futures.Future。

接口

result()


返回future的结果

set_result()

指示future已结束,并赋值。注意,必须显式地调用这个接口,才能给future赋值。

 

import asyncio

# 一个对future进行赋值的函数
async def slow_operation(future):
    await asyncio.sleep(1)
    # 给future赋值
    future.set_result('Future is done!')

loop = asyncio.get_event_loop()
# 创建一个future
future1 = asyncio.Future()
# 使用ensure_future 创建Task
asyncio.ensure_future(slow_operation(future1))
future2 = asyncio.Future()
asyncio.ensure_future(slow_operation(future2))
# gather Tasks,并通过run_uniti_complete来启动、终止loop
loop.run_until_complete(asyncio.gather(future1, future2))
print(future1.result())
print(future2.result())
loop.close()

如果我们注释掉`future.set_result('Future is done!')一行,这个程序将永远不会结束。

TASK

将一个协程的执行过程安排好:用一个future包装起来。Task是Future的一个子类。

Task 负责在实现循环中执行一个协程。 如果被包装的协程由一个future产生,task会暂停被包装协程的执行,等待future的完成。当future完成时,被包装协程会重启,当future结果/异常返回。

事件循环使用协同调度:事件循环每次只能执行1个操作。其他task可以在别的线程的事件循环中执行。当task等待future完成时,事件循环会执行一个新的task。

取消task与取消future不同。调用cancel()将会向被包装的协程抛出CacelledError。如果被包装协程没有捕获CacelledError或者抛出CancelledError时, cancelled()才返回True

太长了,我就不翻译了大意就是说,虽然task的cancel()函数,只会向被包装协程发出抛出一个异常,但是task是否真的canceled取决于被包装协程如何处理这个异常。

不要直接创建task实例,使用ensure_future()函数或者loop.create_task()方法。

任务相关函数

asyncio.ensure_future

安排协程的执行。用future包装它,返回一个task。

asyncio.gather(*coros_or_futures, loop=None, return_exceptions=False)

将多个协程或future,集成为一个future。
所有的future必须在一个事件循环中。如果所有的future都成功完成了,则按照输入顺序(而不是返回顺序)返回所有result。

asyncio.sleep(delay, result=None, *, loop=None)

sleep函数,注意,是可以返回结果的

 

 

本文来自博客园,作者:孙龙-程序员,转载请注明原文链接:https://www.cnblogs.com/sunlong88/articles/9534523.html

posted on 2018-08-25 16:17  孙龙-程序员  阅读(374)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3