協程(Coroutine),又稱微線程,纖程。一般咱們認爲線程是輕量級的進程,所以咱們也把協程理解爲輕量級的線程即微線程。python
協程的做用是在執行函數A時能夠隨時中斷去執行函數B,而後中斷函數B繼續執行函數A(能夠自由切換)。這裏的中斷,不是函數的調用,而是有點相似CPU的中斷。這一整個過程看似像多線程,然而協程只有一個線程執行。多線程
例:併發
def simple_coroutine(): print('-> start') x = yield print(x) print('-> end') #主線程 sc = simple_coroutine() # 可使用sc.send(None),效果同樣 next(sc) #預激 sc.send('go')
執行結果以下:異步
-> start go -> end 拋出StopIteration
simple_coroutine()
是一個生成器,由next(sc)
預激,啓動協程,執行到第一個yield中斷send()
方法給yield傳入參數,繼續執行async
GEN_CLOSED:執行結束函數
終止協程的一種方式:發送某個哨符值,讓協程退出。內置的 None 和Ellipsis 等常量常常用做哨符值oop
asyncio
是Python3.4版本引入的標準庫,直接內置了對異步IO的支持。asyncio
的異步操做,須要在coroutine
中經過yield from
完成。
例:性能
import asyncio @asyncio.coroutine def test(i): print('test_1', i) r = yield from asyncio.sleep(1) print('test_2', i) loop = asyncio.get_event_loop() tasks = [test(i) for i in range(1,3)] loop.run_until_complete(asyncio.wait(tasks)) loop.close()
@asyncio.coroutine
把一個generator
標記爲coroutine
類型,而後就把這個coroutine
扔到EventLoop
中執行。test()
會首先打印出test_1yield from
語法可讓咱們方便地調用另外一個generator
。因爲asyncio.sleep()
也是一個coroutine
,因此線程不會等待asyncio.sleep()
,而是直接中斷並執行下一個消息循環。asyncio.sleep()
返回時,線程就能夠從yield from
拿到返回值(此處是None)asyncio.sleep(1)
當作是一個耗時1秒的IO操做,在此期間主線程並未等待,而是去執行EventLoop
中其餘能夠執行的coroutine
了,所以能夠實現併發執行結果以下:線程
test_1 1 test_1 2 test_2 1 test_2 2
當yield from
後面加上一個生成器後,就實現了生成的嵌套。
實現生成器的嵌套,並非必定必需要使用yield from,而是使用yield from可讓咱們避免讓咱們本身處理各類料想不到的異常,而讓咱們專一於業務代碼的實現。
若是本身用yield去實現,那隻會加大代碼的編寫難度,下降開發效率,下降代碼的可讀性。code
# 子生成器 def average_gen(): total = 0 count = 0 average = 0 while True: new_num = yield average count += 1 total += new_num average = total/count # 委託生成器 def proxy_gen(): while True: yield from average_gen() # 調用方 def main(): calc_average = proxy_gen() next(calc_average)# 預激下生成器 print(calc_average.send(10)) print(calc_average.send(20)) print(calc_average.send(30)) main()
結果以下:
10.0 15.0 20.0
委託生成器的做用:
在調用方與子生成器之間創建一個雙向通道。
雙向通道就是調用方能夠經過send()
直接發送消息給子生成器,而子生成器yield的值,也是直接返回給調用方。
yield from幫咱們作了不少的異常處理,而這些若是咱們要本身去實現的話,一個是編寫代碼難度增長,寫出來的代碼可讀性不好,極可能有遺漏,只要哪一個異常沒考慮到,都有可能致使程序崩潰。
爲了簡化並更好地標識異步IO,從Python 3.5開始引入了新的語法async和await,可讓coroutine的代碼更簡潔易讀。
可將上面的案例代碼改造以下:
import asyncio # @asyncio.coroutine async def test(i): print('test_1', i) # r = yield from asyncio.sleep(1) await asyncio.sleep(1) print('test_2', i) loop = asyncio.get_event_loop() tasks = [test(i) for i in range(1,3)] loop.run_until_complete(asyncio.wait(tasks)) loop.close()
執行結果
test_1 1 test_1 2 test_2 1 test_2 2
async
替換 @asyncio.coroutine
,加在def以前用於修飾await
替換yield from