異步IO:Python中的協程

    我們在實際的問題中會遇到一些堵塞線程的場景,比如UI程序進行耗時的計算阻塞主線程;進行IO操作,比如讀寫文檔或者網絡傳輸時會阻塞線程等等,之前我採用的是使用多進程+多線程的處理方式,但這種方式相比於協程來說都比較慢,特別是在Windows操作系統下啓動一個進程的開銷是巨大的,因此掌握Python中的協程對於處理一些高併發需求來說是非常重要的,看完了廖雪峯的官方網站–異步IO後我總結了一下我對於協程的理解以及yield, yield from asyc ,await等語法的理解

    Python的協程基礎在於生成器,生成器可以在我們需要的時候執行運算,得出執行結果,構造一個生成器有兩種方法。

    一是使用():(x * x for x in range(10)) 列表使用中括號[],生成器使用小括號

    二是在函數中使用yield,此時該函數就不是一個函數,而是一個生成器對象

    我們主要使用第二種方法來定義一個生成器,接下來我介紹對於yield, yield from 以及python3.5的新語法async/await

yield

我以一個簡單的例子來介紹yield語法:

 def test(times):
     return_info = ''
     n = 0
     while n < times:
         n += 1
         recv = yield n
         print("生成器收到數據%s" % recv)
     print("生成器結束")
generator = test(5)

    上述代碼最主要的部分在於 recv = yield n,在等號左邊和後邊將代碼塊分成了兩部分。

    當我們啓動生成器之後,代碼會運行到yield n 後中斷,並將yield 之後的表達式的值返回給generator.send(“要發送的數據”),也就是return_info = generator.send(“要發送的數據”) 此時return_info == n

    當我們再次通過send(“要發送的數據”)啓動生成器時,會從上次中斷的地方開始重新啓動,也就是從等號的左邊開始執行,將send(“要發送的數據”)中的數據賦值給recv,然後繼續執行代碼直到下一次的yield n後中斷。

    我們需要注意的一點是,當我們生成一個生成器generator之後,我們使用generator.send(None)來啓動生成器,此時generator.send(None)相當於next(generator),第一次運行generator時必須send(None),因爲根據yield語法,第一次執行生成器會在yield n 處中斷,此時recv沒有接受值,所以只能爲None,這一部分在後面瞭解了yield的運行機制後就會明白,下面簡單畫一個圖

這裏寫圖片描述

2、yield from

    yield from可以很方便地讓我們調用另一個generator,yield from 建立了用戶端到內層generator之間的”通信”,也可以方便我們在內層generator中處理異常。我們可以看以下代碼:

def inner():
    coef = 1
    total = 0
    while True:
        input_val = yield total
        if input_val is 2:
            print('break')
            break
        total = total + coef * input_val
    print("inner end")

def outer2():
    print("Before inner(), I do this.")
    a = yield from inner()
    print(a)
    print("After inner(), I do that.")

c = outer2()
a = c.send(None)
print(a)
print("first send end")
b = c.send(2)
print(b)

輸出結果爲

Before inner(), I do this.
0
first send end
break
inner end
None
After inner(), I do that.

    當我們啓動生成器時,代碼會執行到yield from inner(),然後進入內層的generator,執行到yield total,此次代碼執行結束,進入中斷。

    當我們第二次啓動生成器時,c.send(2)會將2直接傳遞給內層的input_val,然後執行內層的yield,打印出’break’,內層的yield結束,打印出’inner end’,由於此時是中斷,沒有yield,即沒有返回值,那麼yield from inner()的返回值爲None,打印出a爲None,最後打印結束語句,退出程序。

    此時我們可以看出,yield from會將send的值傳遞到最裏層,再次啓動生成器也是從最裏層的yield開始啓動,然後當內層的yield結束後執行外層的yield直到程序結束。我們可以畫一個示例圖幫助理解

這裏寫圖片描述

3、async/await

    async和await是python3.5之後的新語法,可以用來替換@asyncio.coroutine和await。

    async可以將一個generator標記爲coroutine類型,然後將其放入event_loop中執行,當我們在event_loop中放入多個coroutine的generator後,我們啓動事件循環,此時遇到await,該generator就會中斷,繼續執行下一個generator,直到所有的generator的await執行完成後,event_loop再開始下一次消息循環,此時會再次啓動event_loop中的生成器。

    舉一個形象地例子就是你在快餐店排隊,每一個人在收銀臺付款後(await money),到取餐處等待,直到每個人都付款之後,開始打包外賣,誰的先做好就可以拿走,而不是根據付款的順序來取餐。

    關於異步IO在服務器編程方面用到的地方很多,瞭解了基本的特性後,還需要大量的實際編程經驗才能真正掌握python中協程的概念,繼續去填坑了0.0。。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章