當前位置:係統粉 >   IT資訊 >   微軟資訊 >  不會協程你想拿高薪?那是不存在的!微軟工程師帶來最全協程幹貨

不會協程你想拿高薪?那是不存在的!微軟工程師帶來最全協程幹貨

時間:2017-12-19 來源:互聯網 瀏覽量:

例子1: follow.py 可以使用生成器完成 tail -f 的功能,也就是跟蹤輸出的功能。

不會協程你想拿高薪?那是不存在的!微軟工程師帶來最全協程幹貨(1)

例子2: 生成器用作程序管道(類似unix pipe)

不會協程你想拿高薪?那是不存在的!微軟工程師帶來最全協程幹貨(2)

pipeline.py

不會協程你想拿高薪?那是不存在的!微軟工程師帶來最全協程幹貨(3)

理解pipeline.py

在pipeline中,follow函數和grep函數相當於程序鏈,這樣就能鏈式處理程序。

Yield作為表達【我們開始說協程了~】:

grep.py

不會協程你想拿高薪?那是不存在的!微軟工程師帶來最全協程幹貨(4)

yield最重要的問題在於yield的值是多少。

yield的值需要使用coroutine協程這個概念 相對於僅僅生成值,函數可以動態處理傳送進去的值,而最後值通過yield返回。

協程的執行:

協程的執行和生成器的執行很相似。 當你初始化一個協程,不會返回任何東西。 協程隻能響應run和send函數。 協程的執行依賴run和send函數。 如果你缺誌同道合的朋友!缺學習Python的氛圍!缺入門資料和視頻!缺書籍的PDF!缺遇到問題沒人解答?那就加這個群:103456743 你想要一起學習的朋友?資料免費提供的學習環境?好比圖書館!不收你一分錢,隻為提供一個良好的交流平台!編程貴在多交流!

協程啟動:

所有的協程都需要調用.next( )函數。 調用的next( )函數將要執行到第一個yield表達式的位置。 在yield表達式的位置上,很容易去執行就可以。 協程使用next()啟動。

使用協程的修飾器:

由【協程啟動】中我們知道,啟動一個協程需要記得調用next( )來開始協程,而這個啟動器容易忘記使用。 使用修飾器包一層,來讓我們啟動協程。 【以後所有的協程器都會先有@coroutine

不會協程你想拿高薪?那是不存在的!微軟工程師帶來最全協程幹貨(5)

使用GeneratorExit這個異常類型

拋出一個異常:

在一個協程中,可以拋出一個異常

不會協程你想拿高薪?那是不存在的!微軟工程師帶來最全協程幹貨(6)

不會協程你想拿高薪?那是不存在的!微軟工程師帶來最全協程幹貨(7)

不會協程你想拿高薪?那是不存在的!微軟工程師帶來最全協程幹貨(8)

第三部分,管道過濾器:

叫過濾器其實並不貼切,應該叫中間人Intermediate:其兩端都是send()函數。

不會協程你想拿高薪?那是不存在的!微軟工程師帶來最全協程幹貨(9)

(協程的中間層) 典型的中間層如下:

不會協程你想拿高薪?那是不存在的!微軟工程師帶來最全協程幹貨(10)

協程和生成器的對比

不會協程你想拿高薪?那是不存在的!微軟工程師帶來最全協程幹貨(11)

不同處:生成器使用了迭代器拉取數據,協程使用send()壓入數據。

變得多分支:(上一個協程發送數據去多個下一段協程)

圖示:

不會協程你想拿高薪?那是不存在的!微軟工程師帶來最全協程幹貨(12)

使用協程,你可以發送數據 給 多個 協程過濾器/協程終了。但是請注意,協程源隻是用來傳遞數據的,過多的在協程源中傳遞數據是令人困惑並且複雜的。

一個例子

不會協程你想拿高薪?那是不存在的!微軟工程師帶來最全協程幹貨(13)

從文章中分別打印出含有’python‘ ’ply‘ ’swig‘ 關鍵字的句子。使用了一個協程隊列向所有printer協程 送出 接收到的數據。 圖示:

不會協程你想拿高薪?那是不存在的!微軟工程師帶來最全協程幹貨(14)

或者這樣Hook them up:

不會協程你想拿高薪?那是不存在的!微軟工程師帶來最全協程幹貨(15)

第三部分:協程,事件分發

事件處理

協程可以用在寫各種各樣處理事件流的組件。

介紹一個例子【這個例子會貫穿這個第三部分始終】要求做一個實時的公交車GPS位置監控。編寫程序的主要目的是處理一份文件。傳統上,使用SAX進行處理。【SAX處理可以減少內存空間的使用,但SAX事件驅動的特性會讓它笨重和低效】。

把SAX和協程組合在一起

我們可以使用協程分發SAX事件,比如:

不會協程你想拿高薪?那是不存在的!微軟工程師帶來最全協程幹貨(16)

【最終的組合】

比如,把xml改成json最後從中篩選的出固定信息. buses.py

不會協程你想拿高薪?那是不存在的!微軟工程師帶來最全協程幹貨(17)

協程的一個有趣的事情是,您可以將初始數據源推送到低級別的語言,而不需要重寫所有處理階段。比如,PPT 中69-73頁介紹的,可以通過協程和低級別的語言進行聯動,從而達成非常好的優化效果。如Expat模塊或者cxmlparse模塊。 ps: ElementTree具有快速的遞增xml句法分析

第四部分:從數據處理到並發編程

複習一下上麵學的特點:

協程有以下特點。

協程和生成器非常像。

我們可以用協程,去組合各種簡單的小組件。

我們可以使用創建進程管道,數據流圖的方法去處理數據。

你可以使用伴有複雜數據處理代碼的協程。

一個相似的主題:

我們往協程內傳送數據,向線程內傳送數據,也向進程內傳送數據。那麼,協程自然很容易和線程和分布式係統聯係起來。

基礎的並發:

我們可以通過添加一個額外的層,從而封裝協程進入線程或者子進程。這描繪了幾個基本的概念。

不會協程你想拿高薪?那是不存在的!微軟工程師帶來最全協程幹貨(18)

目標!協程+線程【沒有蛀牙。

下麵看一個線程的例子。 cothread.py

不會協程你想拿高薪?那是不存在的!微軟工程師帶來最全協程幹貨(19)

例子解析:第一部分:先新建一個隊列。然後定義一個永久循環的線程;這個線程可以將其中的元素拉出消息隊列,然後發送到目標裏麵。第二部分:接受上麵送來的元素,並通過隊列,將他們傳送進線程裏麵。其中用到了GeneratorExit ,使得線程可以正確的關閉。

Hook up:cothread.py

不會協程你想拿高薪?那是不存在的!微軟工程師帶來最全協程幹貨(20)

不會協程你想拿高薪?那是不存在的!微軟工程師帶來最全協程幹貨(21)

但是:添加線程讓這個例子慢了50%

目標!協程+子進程

我們知道,進程之間是不共享係統資源的,所以要進行兩個子進程之間的通信,我們需要通過一個文件橋接兩個協程。

不會協程你想拿高薪?那是不存在的!微軟工程師帶來最全協程幹貨(22)

不會協程你想拿高薪?那是不存在的!微軟工程師帶來最全協程幹貨(23)

程序通過sendto()和recvfrom()傳遞文件。

和環境結合的協程:

使用協程,我們可以從一個任務的執行環境中剝離出他的實現。並且,協程就是那個實現。執行環境是你選擇的線程,子進程,網絡等。

需要注意的警告 :

創建大量的協同程序,線程和進程可能是創建 不可維護 應用程序的一個好方法,並且會減慢你程序的速度。需要學習哪些是良好的使用協程的習慣。

在協程裏send()方法需要被適當的同步。

如果你對已經正在執行了的協程使用send()方法,那麼你的程序會發生崩潰。如:多個線程發送數據進入同一個協程。

同樣的不能創造循環的協程:

不會協程你想拿高薪?那是不存在的!微軟工程師帶來最全協程幹貨(24)

堆棧發送正在構建一種調用堆棧(send()函數不返回,直到目標產生)。

如果調用一個正在發送進程的協程,將會拋出一個錯誤。

send() 函數不會掛起任何一個協程的執行。

第五部分:任務一樣的協程

Task的概念

在並發編程中,通常將問題細分為“任務”。 “任務”有下麵幾個經典的特點: * 擁有獨立的控製流。 * 擁有內在的狀態。 * 可以被安排規劃/掛起/恢複。 * 可與其他的任務通信。 協程也是任務的一種。

協程是任務的一種:

下麵的部分 來告訴你協程有他自己的控製流,這裏 if 的控製就是控製流。

不會協程你想拿高薪?那是不存在的!微軟工程師帶來最全協程幹貨(25)

不會協程你想拿高薪?那是不存在的!微軟工程師帶來最全協程幹貨(26)

需要解決的問題(還在複習微嵌知識)

CPU執行的是應用程序,而不是你的操作係統,那 沒有被CPU執行的操作係統 是怎麼控製 正在運行的應用程序 中斷的呢。

中斷(interrupts)和陷阱(Traps)

操作係統隻能通過兩個機製去獲得對應用程序的控製:中斷和陷阱。 * 中斷:和硬件有關的balabala。 * 陷阱:一個軟件發出的信號。 在兩種狀況下,CPU都會掛起正在做的,然後執行OS的代碼(這個時候,OS的代碼成功插入了應用程序的執行),此時,OS來切換了程序。

中斷的底層實現(略…碼字員微嵌隻有70分��‍♀️)

中斷的高級表現:

* 中斷(Traps)使得OS的代碼可以實現。* 在程序運行遇到中斷(Traps)時,OS強製在CPU上停止你的程序。* 程序掛起,然後OS運行。

表現如下圖:

不會協程你想拿高薪?那是不存在的!微軟工程師帶來最全協程幹貨(27)

每次中斷(Traps)程序都會執行另一個不同的任務。

任務調度(非常簡單):

為了執行很多任務,添加一簇任務隊列。

不會協程你想拿高薪?那是不存在的!微軟工程師帶來最全協程幹貨(28)

啟示(很重要):

BB了這麼多微嵌的內容,得到的是什麼結論呢。類比任務調度,協程中yield聲明可以理解為中斷(Traps)。當一個生成器函數碰到了yield聲明,那函數將立即掛起。而執行被傳給生成器函數運行的任何代碼。如果你把yield聲明看成了一個中斷,那麼你就可以組件一個多任務執行的操作係統了。

第七部分:讓我們建一個操作係統。【起飛了,請握好扶手

目標:滿足以下條件建成一個操作係統。

1. 用純python語句。2. 不用線程。3. 不用子進程。4. 使用生成器和協程器。

我們用python去構建操作係統的一些動機:

* 尤其在存在線程鎖(GIL)的條件下,在線程間切換會變得非常重要。我要高並發!* 不阻塞和異步I/O。我要高並發!* 在實戰中可能會遇到:服務器要同時處理上千條客戶端的連接。我要高並發!* 大量的工作 致力於實現 事件驅動 或者說 響應式模型。我要組件化!* 綜上,python構建操作係統,有利於了解現在高並發,組件化的趨勢。

第一步:定義任務

定義一個任務類:任務像一個協程的殼,協程函數傳入target;任務類僅僅有一個run()函數。 pyos1.py

不會協程你想拿高薪?那是不存在的!微軟工程師帶來最全協程幹貨(29)

在foo中,yield就像中斷(Traps)一樣,每次執行run(),任務就會執行到下一個yield(一個中斷)。

第二步:構建調度者

下麵是調度者類,兩個屬性分別是Task隊列和task_id與Task類對應的map。schedule()向隊列裏麵添加Task。new()用來初始化目標函數(協程函數),將目標函數包裝在Task,進而裝入Scheduler。最後mainloop會從隊列裏麵拉出task然後執行到task的target函數的yield為止,執行完以後再把task放回隊列。這樣下一次會從下一個yield開始執行。 pyos2.py

不會協程你想拿高薪?那是不存在的!微軟工程師帶來最全協程幹貨(30)

不會協程你想拿高薪?那是不存在的!微軟工程師帶來最全協程幹貨(31)

第三步:確定任務的停止條件

如果,target函數裏麵不是死循環,那麼上麵的代碼就會出錯。所以我們對Scheduler做改進。添加一個從任務隊列中刪除的操作,和對於StopIteration的驗證。 【對scheduler做改進的原因是任務的性質:可以被安排規劃/掛起/恢複。】

不會協程你想拿高薪?那是不存在的!微軟工程師帶來最全協程幹貨(32)

第四步:添加係統調用基類。

在OS中,中斷是應用程序請求係統服務的方式。在我們的代碼中,OS是調度者(scheduler),而中斷是yield。為了請求調度者服務,任務需要帶值使用yield聲明。 pyos4.py

不會協程你想拿高薪?那是不存在的!微軟工程師帶來最全協程幹貨(33)

代碼解析: 1. 如果taskmap裏麵存在task,就從ready隊列裏麵拿任務出來,如果沒有就結束mainloop。 2. 【就是傳說中的係統調運部分】ready隊列裏麵的task被拿出來以後,執行task,返回一個result對象,並初始化這個result對象。如果隊列裏麵的task要停止迭代了(終止yield這個過程)就從隊列裏刪除這個任務。 3. 最後再通過schedule函數把執行後的task放回隊列裏麵。 4. 係統調用基類,之後所有的係統調用都要從這個基類繼承。

第4.5步:添加第一個係統調用

這個係統調用想返回任務的id。 Task的sendval屬性就像一個係統調用的返回值。當task重新運行的是後,sendval將會傳入這個係統調用。 pyos4.py

不會協程你想拿高薪?那是不存在的!微軟工程師帶來最全協程幹貨(34)

理解這段代碼的前提: (非常重要) 1. send()函數有返回值的,返回值是yield表達式右邊的值。在本段代碼中,result的返回值是yield GetTid()的GetTid的實例或者是yield後麵的。 2. 執行send(sendval)以後,sendval被傳入了yield表達式。並賦給了mytid,返回GetTid()給ruselt。

執行順序: 先創建一個調度者(Scheduler),然後在調度者裏麵添加兩個協程函數:foo(), bar(),最後觸發mainloop進行協程的調度執行。

係統調用原理: 係統調用是基於係統調用類實現的,如GetTid類,其目的是傳出自己的tid。傳出自己的tid之後,再將task放回隊列。

第五步:任務管理

上麵我們搞定了一個GetTid係統調用。我們現在搞定更多的係統調用: * 創建一個新的任務。 * 殺掉一個已經存在的任務。 * 等待一個任務結束。 這些細小的相同的操作會與線程,進程配合。

1. *創建一個新的係統調用*:通過係統調用加入一個task。

不會協程你想拿高薪?那是不存在的!微軟工程師帶來最全協程幹貨(35)

不會協程你想拿高薪?那是不存在的!微軟工程師帶來最全協程幹貨(36)

不會協程你想拿高薪?那是不存在的!微軟工程師帶來最全協程幹貨(37)

不會協程你想拿高薪?那是不存在的!微軟工程師帶來最全協程幹貨(38)

網絡服務器的搭建:

現在已經完成了: * 多任務。 * 開啟新的進程。 * 進行新任務的管理。 這些特點都非常符合一個web服務器的各種特點。下麵做一個Echo Server的嚐試。

不會協程你想拿高薪?那是不存在的!微軟工程師帶來最全協程幹貨(39)

但問題是這個網絡服務器是I / O阻塞的。整個python的解釋器需要掛起,一直到I/O操作結束。

非阻塞的I/O

先額外介紹一個叫Select的模塊。select模塊可以用來監視一組socket鏈接的活躍狀態。用法如下:

不會協程你想拿高薪?那是不存在的!微軟工程師帶來最全協程幹貨(40)

下麵實現一個非阻塞I/O的網絡服務器,所用的思想就是之前所實現的Task waiting 思想。

不會協程你想拿高薪?那是不存在的!微軟工程師帶來最全協程幹貨(41)

源碼解析:__init__裏麵的是兩個字典。用來存儲阻塞的IO的任務。waitforread()和waitforwrite()將需要等待寫入和等待讀取的task放在dict裏麵。這裏的iopoll():使用select()去決定使用哪個文件描述器,並且能夠不阻塞任意一個和I/O才做有關係的任務。poll這個東西也可以放在mainloop裏麵,但是這樣會帶來線性的開銷增長。 詳情請見: Python Select 解析 - 金角大王 - 博客園

添加新的係統調用:

不會協程你想拿高薪?那是不存在的!微軟工程師帶來最全協程幹貨(42)

更多請見開頭那個連接裏麵的代碼:pyos8.py

這樣我們就完成了一個多任務處理的OS。這個OS可以並發執行,可以創建、銷毀、等待任務。任務可以進行I/O操作。並且最後我們實現了並發服務器。

第八部分:協程棧的一些問題的研究。

我們可能在使用yield的時候會遇到一些問題:

不會協程你想拿高薪?那是不存在的!微軟工程師帶來最全協程幹貨(43)

讓我們來看看這個叫蹦床的奇淫巧技。

代碼:trampoline.py

不會協程你想拿高薪?那是不存在的!微軟工程師帶來最全協程幹貨(44)

整個控製流如下:

不會協程你想拿高薪?那是不存在的!微軟工程師帶來最全協程幹貨(45)

我們看到,上圖中左側為統一的scheduler,如果我們想調用一個子線程,我們都用通過上麵的scheduler進行調度。

不會協程你想拿高薪?那是不存在的!微軟工程師帶來最全協程幹貨(46)

千萬不要一個函數裏麵包含兩個或多個以上的功能,比如函數是generator就是generator,是一個coroutine就是一個coroutin。

謝謝閱讀!

不會協程你想拿高薪?那是不存在的!微軟工程師帶來最全協程幹貨(47)

如有侵權請聯係小編刪除哦!

我要分享:

最新熱門遊戲

版權信息

Copyright @ 2011 係統粉 版權聲明 最新發布內容 網站導航