定义一个协程

  • 通过 async 关键字定义一个协程。

  • 通过 await 关键字挂起一个协程或 future 对象。

  • 调用协程返回一个协程对象。

  • 把协程对象或 task 对象放入事件循环中执行。

  1. import asyncio
  2. async def f():
  3. await asyncio.sleep(5) # 挂起一个协程
  4. print('5秒后输出此句')
  5. cor = f()
  6. print(cor) # 输出:<coroutine object f at 0x01EDB720>
  7. loop = asyncio.get_event_loop()
  8. loop.run_until_complete(cor) # 把协程对象放入事件循环中执行。

task 对象

实际上,只是把 coroutine 对象包装一下,加了个状态,在加入事件循环之前是 pending,执行后是 finished 。
run_until_complete 的参数是 future 对象,当传入一个协程对象时会包装成 task 对象,而 Task 是 Future 的子类。

  1. task = loop.create_task(cor)
  2. print(task) # <Task pending coro=<f() running at a.py:4>>
  3. loop.run_until_complete(task)
  4. print(task) # <Task finished coro=<f() done, defined at a.py:4> result=None>

task 绑定回调

回调函数的最后一个参数是一个 future 对象,就是之前创建的 task 对象,保存着协程的返回结果。

  1. import asyncio
  2. async def f():
  3. await asyncio.sleep(1)
  4. print('1秒后输出此句')
  5. return 520
  6. cor = f()
  7. task = asyncio.ensure_future(cor) # 把协程包装成 task 对象
  8. def callback(future):
  9. print(future.result()) # 520
  10. print(future is task) # True
  11. task.add_done_callback(callback) # 绑定回调
  12. loop = asyncio.get_event_loop()
  13. loop.run_until_complete(task) 传入 task 对象

异步

终于来到了异步,上面的例子都只有一个协程,当一个事件循环中有多个协程时,就能体现出异步的好处。

但是,有一个问题,不能直接把多个协程(或多个 task)一下子传入 rununtil_complete 。需要多个 task 包装成一个 task。
可以用 asyncio.gather(*_coros_or_futures
)、asyncio.wait(futures) 来包装。

  • gather 返回一个future 对象,多变一,协程返回结果就保存在这个对象。

  • wait 返回 (done, pending),done 和 future 都是 future 对象,多变二。

  1. import asyncio
  2. async def f(x):
  3. await asyncio.sleep(x)
  4. print('{}秒后输出此句'.format(x))
  5. return x
  6. tasks = map(asyncio.ensure_future, [f(1), f(3), f(5)])
  7. loop = asyncio.get_event_loop()
  8. loop.run_until_complete(asyncio.gather(*tasks))
  9. # loop.run_until_complete(asyncio.wait(tasks))
  10. loop.close()

gather 和 wait 函数返回的 future 除了可以传给 run_until_complete 外,还可以从这个 future 中获取多个协程的返回结果

获取协程返回值

  1. import asyncio
  2. async def f(x):
  3. await asyncio.sleep(x)
  4. print('{}秒后输出此句'.format(x))
  5. return x
  6. async def main():
  7. tasks = map(asyncio.ensure_future, [f(1), f(3), f(5)])
  8. results = await asyncio.gather(*tasks) # 从返回的 future 中获取多个协程结果
  9. for r in results:
  10. print(r)
  11. loop = asyncio.get_event_loop()
  12. loop.run_until_complete(main())
  13. loop.close()

asyncio.wait 的做法:

  1. dones, pendings = await asyncio.wait(tasks)
  2. for r in dones:
  3. print(r.result())

还有一种方法获取协程返回值:asyncio.as_completed(futures),前面的 asyncio.wait、asyncio.gather 是把所有任务搞在一起,所有任务完成时在一起返回,而 asyncio.as_completed 每完成一个任务就返回对应的协程的返回值。
as_completed 返回一个迭代器,里面的值是 future 实例,所以 for 循环里面 await future 。

  1. for future in asyncio.as_completed(tasks):
  2. r = await future
  3. print(r)

还可以这样干,把 main 当作中间层,用来把最里层 f 的结果传递给最外层模块:

  1. import asyncio
  2. async def f(x):
  3. await asyncio.sleep(x)
  4. print('{}秒后输出此句'.format(x))
  5. return x # 返回协程结果
  6. async def main():
  7. tasks = map(asyncio.ensure_future, [f(1), f(3), f(5)])
  8. return await asyncio.gather(*tasks) # 返回多个协程返回值,也可用 asyncio.wait 干
  9. loop = asyncio.get_event_loop()
  10. results = loop.run_until_complete(main()) # 取得多个协程返回值
  11. for r in results:
  12. print(r)
  13. loop.close()

很奇怪 run_until_complete 的作用域层,不能出现 gather、wait、as_completed 方法获取协程返回值。

参考文档:
https://www.jianshu.com/p/b5e347b3a17c