PageRenderTime 23ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/content/python/python-asyncio-note-control-Coroutines.rst

https://gitlab.com/mozillazg/blog
ReStructuredText | 380 lines | 294 code | 86 blank | 0 comment | 0 complexity | 6bec941ddb6ca830d54732a7758fc9a3 MD5 | raw file
  1. asyncio 学习笔记控制组合式 Coroutines
  2. =======================================
  3. :slug: python-asyncio-note-control-Coroutines
  4. :date: 2017-08-25
  5. :tags: asyncio
  6. 本文是 https://pymotw.com/3/asyncio/control.html
  7. 的学习笔记强烈推荐直接阅读原文
  8. 对于线性执行的 Coroutines 可以很方便的通过 ``await`` 来控制
  9. 对于组合式的 Coroutines比如在一个 coroutine 中等待其他并发执行的
  10. Coroutines 完成的操作也可以通过 asyncio 模块来实现
  11. 等待多个 Coroutines
  12. -------------------
  13. | 在一个 Coroutine 中等待其他多个 Coroutines
  14. 操作完成是一个很常见的需求比如下载一批数据执行对顺序没有要求只想要最后的结果
  15. | ``wait()`` 方法可以实现暂停当前 Coroutine 直到后台其他 Coroutines
  16. 操作完成
  17. .. code:: python
  18. # asyncio_wait.py
  19. import asyncio
  20. async def phase(i):
  21. print('in phase {}'.format(i))
  22. await asyncio.sleep(0.1 * i)
  23. print('done with phase {}'.format(i))
  24. return 'phase {} result'.format(i)
  25. async def main(num_phases):
  26. print('starting main')
  27. phases = [
  28. phase(i)
  29. for i in range(num_phases)
  30. ]
  31. print('waiting for phases to complete')
  32. completed, pending = await asyncio.wait(phases)
  33. results = [t.result() for t in completed]
  34. print('results: {!r}'.format(results))
  35. event_loop = asyncio.get_event_loop()
  36. try:
  37. event_loop.run_until_complete(main(3))
  38. finally:
  39. event_loop.close()
  40. ``wait`` 内部它使用一个集合来保存它创建的 ``Task``
  41. 实例所以它保存的 ``Task`` 的结果是无序的\ ``wait``
  42. 返回一个由两个集合组成的元祖一个保存状态为 done ``Task``
  43. 一个保存状态为 pending ``Task``:
  44. ::
  45. $ python3.6 asyncio_wait.py
  46. starting main
  47. waiting for phases to complete
  48. in phase 0
  49. in phase 2
  50. in phase 1
  51. done with phase 0
  52. done with phase 1
  53. done with phase 2
  54. results: ['phase 0 result', 'phase 2 result', 'phase 1 result']
  55. 当调用 ``wait`` 时指定 ``timeout`` 参数才会有可能出现结果中包含状态为
  56. pending ``Task``:
  57. .. code:: python
  58. # asyncio_wait_timeout.py
  59. import asyncio
  60. async def phase(i):
  61. print('in phase {}'.format(i))
  62. try:
  63. await asyncio.sleep(0.1 * i)
  64. except asyncio.CancelledError:
  65. print('phase {} canceled'.format(i))
  66. raise
  67. else:
  68. print('done with phase {}'.format(i))
  69. return 'phase {} result'.format(i)
  70. async def main(num_phases):
  71. print('starting main')
  72. phases = [
  73. phase(i)
  74. for i in range(num_phases)
  75. ]
  76. print('waiting 0.1 for phases to complete')
  77. completed, pending = await asyncio.wait(phases, timeout=0.1)
  78. print('{} completed and {} pending'.format(
  79. len(completed), len(pending),
  80. ))
  81. if pending:
  82. print('canceling tasks')
  83. for t in pending:
  84. t.cancel()
  85. print('exiting main')
  86. event_loop = asyncio.get_event_loop()
  87. try:
  88. event_loop.run_until_complete(main(3))
  89. finally:
  90. event_loop.close()
  91. 对于 pending task 最好是把它们 cancel
  92. 否则事件循环在之后会继续执行它们或者退出程序的时候会有警告信息.
  93. ::
  94. $ python3.6 asyncio_wait_timeout.py
  95. starting main
  96. waiting 0.1 for phases to complete
  97. in phase 0
  98. in phase 2
  99. in phase 1
  100. done with phase 0
  101. 1 completed and 2 pending
  102. canceling tasks
  103. exiting main
  104. phase 1 canceled
  105. phase 2 canceled
  106. cancel 会警告的情况:
  107. .. code:: python
  108. # asyncio_wait_timeout_without_cancel.py
  109. import asyncio
  110. async def phase(i):
  111. print('in phase {}'.format(i))
  112. try:
  113. await asyncio.sleep(0.1 * i)
  114. except asyncio.CancelledError:
  115. print('phase {} canceled'.format(i))
  116. raise
  117. else:
  118. print('done with phase {}'.format(i))
  119. return 'phase {} result'.format(i)
  120. async def main(num_phases):
  121. print('starting main')
  122. phases = [
  123. phase(i)
  124. for i in range(num_phases)
  125. ]
  126. print('waiting 0.1 for phases to complete')
  127. completed, pending = await asyncio.wait(phases, timeout=0.1)
  128. print('{} completed and {} pending'.format(
  129. len(completed), len(pending),
  130. ))
  131. print('exiting main')
  132. event_loop = asyncio.get_event_loop()
  133. try:
  134. event_loop.run_until_complete(main(3))
  135. finally:
  136. event_loop.close()
  137. 运行结果:
  138. ::
  139. $ python3.6 asyncio_wait_timeout_without_cancel_warn.py
  140. starting main
  141. waiting 0.1 for phases to complete
  142. in phase 1
  143. in phase 0
  144. in phase 2
  145. done with phase 0
  146. 1 completed and 2 pending
  147. exiting main
  148. done with phase 1
  149. Task was destroyed but it is pending!
  150. task: <Task pending coro=<phase() done, defined at asyncio_wait_timeout_without_cancel_warn.py:5> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x10e227918>()]>>
  151. pending 还会继续执行的情况:
  152. .. code:: python
  153. # asyncio_wait_timeout_without_cancel_continue.py
  154. import asyncio
  155. async def phase(i):
  156. print('in phase {}'.format(i))
  157. try:
  158. await asyncio.sleep(0.1 * i)
  159. except asyncio.CancelledError:
  160. print('phase {} canceled'.format(i))
  161. raise
  162. else:
  163. print('done with phase {}'.format(i))
  164. return 'phase {} result'.format(i)
  165. async def main(num_phases):
  166. print('starting main')
  167. phases = [
  168. phase(i)
  169. for i in range(num_phases)
  170. ]
  171. print('waiting 0.1 for phases to complete')
  172. completed, pending = await asyncio.wait(phases, timeout=0.1)
  173. print('{} completed and {} pending'.format(
  174. len(completed), len(pending),
  175. ))
  176. print('exiting main')
  177. event_loop = asyncio.get_event_loop()
  178. try:
  179. event_loop.run_until_complete(main(3))
  180. event_loop.run_until_complete(asyncio.sleep(3))
  181. finally:
  182. event_loop.close()
  183. 运行结果
  184. ::
  185. $ python3.6 asyncio_wait_timeout_without_cancel_continue.py
  186. starting main
  187. waiting 0.1 for phases to complete
  188. in phase 1
  189. in phase 0
  190. in phase 2
  191. done with phase 0
  192. 1 completed and 2 pending
  193. exiting main
  194. done with phase 1
  195. done with phase 2
  196. 收集 Coroutines 结果
  197. --------------------
  198. 如果 Coroutines
  199. 是在程序中显示生成的并且只关心返回值结果的话\ ``gather()``
  200. 是一种比较好的收集多个操作结果的方法
  201. .. code:: python
  202. # asyncio_gather.py
  203. import asyncio
  204. async def phase1():
  205. print('in phase1')
  206. await asyncio.sleep(2)
  207. print('done with phase1')
  208. return 'phase1 result'
  209. async def phase2():
  210. print('in phase2')
  211. await asyncio.sleep(1)
  212. print('done with phase2')
  213. return 'phase2 result'
  214. async def main():
  215. print('starting main')
  216. print('waiting for phases to complete')
  217. results = await asyncio.gather(
  218. phase1(),
  219. phase2()
  220. )
  221. print('results: {!r}'.format(results))
  222. event_loop = asyncio.get_event_loop()
  223. try:
  224. event_loop.run_until_complete(main())
  225. finally:
  226. event_loop.close()
  227. 通过 gather 创建的 task
  228. 对外部是不可见的所以它们不能被取消返回值是按输入参数顺序保存的对应
  229. coroutine
  230. 的执行结果无论真正执行的时候是否按顺序执行的最终的结果都是有序的
  231. ::
  232. $ python3.6 asyncio_gather.py
  233. starting main
  234. waiting for phases to complete
  235. in phase2
  236. in phase1
  237. done with phase2
  238. done with phase1
  239. results: ['phase1 result', 'phase2 result']
  240. 当后台操作完成的时候做一些事情
  241. ------------------------------
  242. | ``as_completed()`` 是一个生成器它将管理传入的 coroutines 执行
  243. | 每次迭代都将返回一个 coroutine 执行完成的 task
  244. | ``wait()`` 一样\ ``as_completed()`` 也不会保证顺序 ``wait()``
  245. 的区别就是它不会等待所有的
  246. | coroutine 操作都完成以后才能做其他操作
  247. .. code:: python
  248. # asyncio_as_completed.py
  249. import asyncio
  250. async def phase(i):
  251. print('in phase {}'.format(i))
  252. await asyncio.sleep(0.5 - (0.1 * i))
  253. print('done with phase {}'.format(i))
  254. return 'phase {} result'.format(i)
  255. async def main(num_phases):
  256. print('starting main')
  257. phases = [
  258. phase(i)
  259. for i in range(num_phases)
  260. ]
  261. print('waiting for phases to complete')
  262. results = []
  263. for next_to_complete in asyncio.as_completed(phases):
  264. answer = await next_to_complete
  265. print('recevived answer {!r}'.format(answer))
  266. results.append(answer)
  267. print('results: {!r}'.format(results))
  268. return results
  269. event_loop = asyncio.get_event_loop()
  270. try:
  271. event_loop.run_until_complete(main(3))
  272. finally:
  273. event_loop.close()
  274. 结果:
  275. ::
  276. $ python3.6 asyncio_as_completed.py
  277. starting main
  278. waiting for phases to complete
  279. in phase 1
  280. in phase 2
  281. in phase 0
  282. done with phase 2
  283. recevived answer 'phase 2 result'
  284. done with phase 1
  285. recevived answer 'phase 1 result'
  286. done with phase 0
  287. recevived answer 'phase 0 result'
  288. results: ['phase 2 result', 'phase 1 result', 'phase 0 result']
  289. 参考资料
  290. --------
  291. - `Composing Coroutines with Control Structures — PyMOTW
  292. 3 <https://pymotw.com/3/asyncio/control.html>`__
  293. - `18.5.3. Tasks and coroutines — Python 3.6.2
  294. documentation <https://docs.python.org/3.6/library/asyncio-task.html>`__