摘要:所以在第一遍閱讀官方文檔的時候,感覺完全是在夢游。通過或者等待另一個協程的結果或者異常,異常會被傳播。接口返回的結果指示已結束,并賦值。取消與取消不同。調用將會向被包裝的協程拋出。任務相關函數安排協程的執行。負責切換線程保存恢復。
Tasks and coroutines
翻譯的python官方文檔
這個問題的惡心之處在于,如果你要理解coroutine,你應該理解future和task。而你如果想理解future和task你應該先理解coroutine。所以在第一遍閱讀官方文檔的時候,感覺完全是在夢游。但讀到第二遍和第三遍的時候,就清楚很多了。
Coroutines協程(coroutine)包括兩個概念:
協程函數(async def 或者 @asyncio.coroutine)
協程函數所返回的協程對象。
協程功能:
通過result = await future或者 result = yeild from future,懸掛協程,直到future完成,獲取future的結果/異常(參見下面對future及future結果的描述,或等看完future之后回來再閱讀這一段)。
通過 result = await coroutine 或者 result = yeild from coroutine 等待另一個協程的結果(或者異常,異常會被傳播)。
returen expression 返回該協程的結果,被await,或者yield from獲取。
raise exception,拋出異常,被await,或者yield from獲取。
調用協程函數并不能使該協程運行。調用協程函數所返回的協程對象,在被你安排執行之前,不會做任何事情。有兩種方式可以啟動它:
通過在一個已經啟動的協程中調用:await coroutine或者yield from coroutine
或者通過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)協程其實是在不停返回結果的。最后的結果才會被返回。
futurefuture是一個容器,或者占位符(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!")一行,這個程序將永遠不會結束。
TASKSchedule the execution of a coroutine: wrap it in a future. Task is a subclass of Future.
將一個協程的執行過程安排好:用一個future包裝起來。Task是Future的一個子類。
A task is responsible for executing a coroutine object in an event loop. If the wrapped coroutine yields from a future, the task suspends the execution of the wrapped coroutine and waits for the completion of the future. When the future is done, the execution of the wrapped coroutine restarts with the result or the exception of the future.
Task 負責在實現循環中執行一個協程。 如果被包裝的協程由一個future產生,task會暫停被包裝協程的執行,等待future的完成。當future完成時,被包裝協程會重啟,當future結果/異常返回。
Event loops use cooperative scheduling: an event loop only runs one task at a time. Other tasks may run in parallel if other event loops are running in different threads. While a task waits for the completion of a future, the event loop executes a new task.
事件循環使用協同調度:事件循環每次只能執行1個操作。其他task可以在別的線程的事件循環中執行。當task等待future完成時,事件循環會執行一個新的task。
The cancellation of a task is different from the cancelation of a future. Calling cancel() will throw a CancelledError to the wrapped coroutine. cancelled() only returns True if the wrapped coroutine did not catch the CancelledError exception, or raised a CancelledError exception.
取消task與取消future不同。調用cancel()將會向被包裝的協程拋出CacelledError。如果被包裝協程沒有捕獲CacelledError或者拋出CancelledError時, cancelled()才返回True
這里可以參考Task源碼中的一段注釋:
Request that this task cancel itself.
This arranges for a CancelledError to be thrown into the wrapped coroutine on the next cycle through the event loop. The coroutine then has a chance to clean up or even deny the request using try/except/finally. Unlike Future.cancel, this does not guarantee that the task will be cancelled: the exception might be caught and acted upon, delaying cancellation of the task or preventing cancellation completely. The task may also return a value or raise a different exception. Immediately after this method is called, Task.cancelled() will not return True (unless the task was already cancelled). A task will be marked as cancelled when the wrapped coroutine terminates with a CancelledError exception (even if cancel() was not called)
太長了,我就不翻譯了大意就是說,雖然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函數,注意,是可以返回結果的
一些參考資料
awesome asyncio
參考這篇文章
線程是操作系統層面的“并行”, 協程是應用程序層面的“并行”。
協程本質上就是:提供一個環境,保存一些需要等待的任務,當這些任務可以執行(等待結束)的時候,能夠執行。再等待的過程中,程序可以執行別的任務。
以下內容參考自:PYTHON: GENERATORS, COROUTINES, NATIVE COROUTINES AND ASYNC/AWAIT
@asyncio.coroutine def foo(): yield from ....
async def foo(): await ......
注意在@asyncio.coroutine里只能是 yield from, 在async中,只能是await。
你可以通過@type.coroutine裝飾器,降一個generator變為一個可await得協程。
Asynchronous Python
多線程:創建多個線程,每個線程處理一個任務。會競爭資源、死鎖什么的。CPU負責切換線程、保存恢復context。
Asnycio的文檔,但是感覺寫的一般,有些語焉不詳。
引用了一片關于線程的文章,還沒看
不用gevent的原因,是因為gevent還是使用了線程,而線程是難以調試的。
Some thoughts on asynchronous API design in a post-async/await world
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/41135.html
摘要:主程序通過喚起子程序并傳入數據,子程序處理完后,用將自己掛起,并返回主程序,如此交替進行。通過輪詢或是等事件框架,捕獲返回的事件。從消息隊列中取出記錄,恢復協程函數。然而事實上只有直接操縱的協程函數才有可能接觸到這個對象。 首發于 我的博客 轉載請注明出處 寫在前面 本文默認讀者對 Python 生成器 有一定的了解,不了解者請移步至生成器 - 廖雪峰的官方網站。 本文基于 Pyth...
摘要:譯者說于年月日發布,該版本正式支持的關鍵字,并且用舊版本編譯同樣可以使用這兩個關鍵字,這無疑是一種進步。其次,這是最后一個支持和的版本了,在后續的版本了會移除對它們的兼容。 譯者說 Tornado 4.3于2015年11月6日發布,該版本正式支持Python3.5的async/await關鍵字,并且用舊版本CPython編譯Tornado同樣可以使用這兩個關鍵字,這無疑是一種進步。其次...
摘要:是之后引入的標準庫的,這個包使用事件循環驅動的協程實現并發。沒有能從外部終止線程,因為線程隨時可能被中斷。上一篇并發使用處理并發我們介紹過的,在中,只是調度執行某物的結果。 asyncio asyncio 是Python3.4 之后引入的標準庫的,這個包使用事件循環驅動的協程實現并發。asyncio 包在引入標準庫之前代號 Tulip(郁金香),所以在網上搜索資料時,會經常看到這種花的...
閱讀 3516·2021-09-27 13:35
閱讀 3556·2019-08-29 17:09
閱讀 2425·2019-08-26 11:30
閱讀 697·2019-08-26 10:32
閱讀 532·2019-08-26 10:23
閱讀 1193·2019-08-26 10:20
閱讀 3149·2019-08-23 15:26
閱讀 3550·2019-08-23 14:33