推文:玩轉 Python 3.5 的 await/asyncpython
def func1(): print("func1 start") print("func1 end") def func2(): print("func2 start") print("func2 a") print("func2 b") print("func2 c") print("func2 end") func1() func2()
func1 start
func1 end
func2 start
func2 a
func2 b
func2 c
func2 end
沒法實現兩個函數之間的交互。async
async def func1(): print("func1 start") print("func1 end") async def func2(): print("func2 start") print("func2 a") print("func2 b") print("func2 c") print("func2 end") f1 = func1() f2 = func2() print(f1,f2)
<coroutine object func1 at 0x000000000084C468> <coroutine object func2 at 0x000000000084CD58> sys:1: RuntimeWarning: coroutine 'func1' was never awaited #警告信息 sys:1: RuntimeWarning: coroutine 'func2' was never awaited
#上面再也不只是函數,而變爲協程對象,協程在調用時,不會被執行
關鍵之處是協程確實是與 Python 的生成器很是類似,也都有一個 send 方法。咱們能夠經過調用 send 方法來啓動一個協程的執行。函數
f1 = func1() f2 = func2() try: print('f1.send') f1.send(None) except StopIteration as e: #這裏也是須要去捕獲StopIteration方法 pass try: print('f1.send') f2.send(None) except StopIteration as e: pass
StopIteration 異常是一種標記生成器(或者像這裏的協程)執行結束的機制。雖然這是一個異常,可是確實是咱們指望的!咱們能夠用適當的 try-catch 代碼將其包起來,這樣就能夠避免錯誤提示。spa
協程與線程類似的地方是多個線程之間也能夠交替執行,不過與線程不一樣之處在於協程之間的切換是顯式的,而線程是隱式的(大多數狀況下是更好的方式)。因此咱們須要加入顯式切換的代碼。.net
咱們可能根據生成器或者協程gevent的使用,而去推測這種方案:(是錯誤的)線程
async def func1(): print("func1 start") yield print("func1 end")
不容許在本地協程函數中使用yield,可是做爲替換,咱們可使用到await 表達式來暫停協程的執行。注意await _something_,這裏的_something_表明的是基於生成器的協程對象,或者一個特殊的相似 Future 的對象。code
import types @types.coroutine def switch(): yield async def func1(): print("func1 start") await switch() print("func1 end") async def func2(): print("func2 start") print("func2 a") print("func2 b") print("func2 c") print("func2 end") f1 = func1() f2 = func2() try: f1.send(None) except StopIteration as e: pass try: f2.send(None) except StopIteration as e: pass
func1 start
func2 start
func2 a
func2 b
func2 c
func2 end
注意上面的輸出:只是執行了函數的轉換,有func1轉換到func2,並無再回到func1中。那麼如何讓其在回到func1中呢。協程
import types @types.coroutine def switch(): yield async def func1(): print("func1 start") await switch() print("func1 end") async def func2(): print("func2 start") print("func2 a") print("func2 b") print("func2 c") print("func2 end") f1 = func1() f2 = func2() try: f1.send(None) except StopIteration as e: pass try: f2.send(None) except StopIteration as e: pass try: f1.send(None) except StopIteration as e: pass
func1 start
func2 start
func2 a
func2 b
func2 c
func2 end
func1 end
上面能夠實現兩個函數交替一次,回到原協程中。可是咱們更加但願接二連三的調用send驅動不一樣的協程去執行,直到send拋出 StopIteration 異常。對象
爲此咱們blog
import types @types.coroutine def switch(): yield async def func1(): print("func1 start") await switch() print("func1 e") await switch() print("func1 end") async def func2(): print("func2 start") print("func2 a") await switch() print("func2 b") print("func2 c") await switch() print("func2 end") def run(task_list): coro_list = list(task_list) while coro_list: for coro in list(coro_list): try: coro.send(None) except StopIteration: coro_list.remove(coro) f1 = func1() f2 = func2()
run([f1,f2])