python异步编程案例之Future和Task

Future,是对协程的封装,代表一个异步操作的最终结果,其值会在将来被计算出来。当一个Future对象被await的时候,表示当前的协程会持续等待,直到 Future对象所指向的异步操作执行完毕。日常开发基本不需要直接用这个底层 Future 类。可以对Future 实例添加完成后的回调 (add_done_callback)、取消任务 (cancel)、设置最终结果 (set_result)、设置异常 (如果有的话,set_exception) 等。

Task(Task是Future子类)是跟事件循环交互的一种主要方式。创建Task意思就是把coroutine封装成Task实例,并追踪 coroutine 的完成状态。也就是说保存了协程运行后的状态,用于未来获取协程的结果。Task是Future 的子类,因此这个Task实例可以作为变量传给其他代码。也就是说其他代码可以直接await这个Task实例,并且不需要知道原始的coroutine是如何被创建的,并且在 Task实例完成时还可以获取coroutine的返回值。
Task完成有三种情况:
封装的协程已返回
封装的协程已抛出异常
Task被取消

两种创建Task实例的方式,asyncio.ensure_future 和 loop.create_task(python 3.7以后的版本支持asyncio.create_task)。

对于绝大多数场景要并发执行的是协程,所以直接用asyncio.create_task就足够了。注意:如果当前线程中没有正在运行的事件循环,asyncio.create_task将会引发RuntimeError异常。

asyncio.ensure_future,确保返回是一个 Future 对象。除了接受协程,还可以是 Future 对象或者 awaitable 对象。

  1. 如果参数是协程,其实底层还是用的 loop.create_task,返回 Task 对象
  2. 如果是 Future 对象会直接返回
  3. 如果是一个 awaitable 对象会 await 这个对象的__await__方法,再执行一次 ensure_future,最后返回 Task 或者 Future

Future有四种状态:Pending, Running, Done, Cancelled
创建Task实例后,Task实例在加入事件循环之前是pending状态。事件循环调用执行的时候当然就是running,调用完毕自然就是done,如果需要停止事件循环,就需要先把task取消。
注意:coroutine封装为Task后不会立马启动,当某个代码await这个task的时候才会被执行
什么样的对象是awaitable 的呢?类里包含了一个__await__方法。主要有三类awaitable对象:协程coroutine,任务Task,未来对象Future。
为何await 后 Future 的状态就能改变?因为用loop.run_in_executor创建的 Future 注册了一个回调(通过asyncio.futures.wrap_future,加了一个_call_set_state回调。

那为什么要存在 Future 和 Task 这 2 种类型呢? 开发者通常并不需要直接操作 Future 这种底层对象,而是用 Future 的子类 Task 协同的调度协程以实现并发。

import time
import asyncio
now = lambda : time.perf_counter()
async def do_some_work(x):
    print("Hello:",x)
    return "work is done for {}".format(x)
async def main():
    tasks = []
    for i in range(5):
        coroutine = do_some_work(2)
        # task = asyncio.ensure_future(coroutine)
        task = loop.create_task(coroutine)
        tasks.append(task)
    # dones, pendings = await asyncio.wait(tasks)
    # for task in dones:
    #     print("task result:", task.result())
    results = await asyncio.gather(*tasks)
    for result in results:
        print("Task result:",result)
start = now()
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_until_complete(main()) # 将协程注册到事件循环,并启动事件循环
loop.close()   
end = now()
print("total run time:", end-start)
发布了24 篇原创文章 · 获赞 0 · 访问量 899

猜你喜欢

转载自blog.csdn.net/HighDS/article/details/103883245