翻譯的python官方文檔html
這個問題的噁心之處在於,若是你要理解coroutine
,你應該理解future
和task
。而你若是想理解future
和task
你應該先理解coroutine
。因此在第一遍閱讀官方文檔的時候,感受徹底是在夢遊。但讀到第二遍和第三遍的時候,就清楚不少了。python
協程(coroutine
)包括兩個概念:git
async def
或者 @asyncio.coroutine
)協程功能:github
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
獲取。調用協程函數並不能使該協程運行。調用協程函數所返回的協程對象,在被你安排執行以前,不會作任何事情。有兩種方式能夠啓動它:express
await coroutine
或者yield from coroutine
ensure_task()
以及loop.create_task()
安排協程的執行。只有事件循環在運行的時候,協程才能運行api
在本文檔中,有些普通函數返回了一個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.pyapp
# 這段代碼和上面的代碼執行結果是相同的。只不過用了另外一種調用協程的方式 # 先在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)
協程實際上是在不停返回結果的。最後的結果纔會被返回。less
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!')一行,這個程序將永遠不會結束。
Schedule 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
得協程。\
多線程:建立多個線程,每一個線程處理一個任務。會競爭資源、死鎖什麼的。CPU負責切換線程、保存恢復context。
Asnycio的文檔,可是感受寫的通常,有些語焉不詳。
引用了一片關於線程的文章,還沒看
不用gevent
的緣由,是由於gevent
仍是使用了線程,而線程是難以調試的。
Some thoughts on asynchronous API design in a post-async/await world