python eventlet協程基礎

2021-10-02 06:59:31 字數 2872 閱讀 9971

一、協程相關

1.greenlet

greenlet在import的時候為一物件,即greenlet在程式中為一單例物件。greenlet主要實現了儲存函式的棧頂和棧底、函式在堆中的位址和長度。

greenlet實現了乙個類似於longjump的功能,用於在函式中執行切換。yeild實現了帶返回值的longjump。greenlet的資料結構中記錄了父物件的位址,當函式執行switch的時候將返回到父物件中,由父物件對其進行排程。

2.eventlet模型

greenlet實現了「跳出去」,eventlet增加了hub的概念,實現了「切回來」。

eventlet將i/o操作、訊號等待等阻塞操作都進行了重構。eventlet將阻塞事件註冊到多路復用模型中,當當前執行緒阻塞的時候會在排程模型中輪詢,如果事件就緒則執行對應的協程。因此在編碼過程中,預設無阻塞的情況下python將執行當前協程而不會切換。

3.spawn/spawn_n

spawn/spawn_n使用定時器將協程先掛起,在spawn執行後,對應協程並未孵化出來,需要當前協程阻塞讓出排程系統才會去處理定時器,在處理定時器的時候將對應的協程孵化出來。

4.openstack api啟動流程

wsgi.set_eventlet_hub()

eventlet.hubs.get_hub().clock = monotonic.monotonic

openstack api.conf

server.wait()

wsgi.set_eventlet_hub:將hub從epoll改為poll。

eventlet.hubs.get_hub().clock = monotonic.monotonic:將定時器從系統時間改為晶振時間。

server.start:建立socket、註冊訊號處理函式(worker == 0不需要)、拉子程序

主程序:登出socket,server.wait等待子程序結束

子程序:起執行緒池,執行eventlet.wsgi.server

5.eventlet.wsgi

該函式為一死迴圈(可將is_accepting置為false結束),迴圈執行socket.accept,當有請求時建立連線,並將連線作為引數孵化乙個router協程,當accept阻塞的時候排程到協程中。

二、python物件模型

1.基礎python物件

在python中,所有變數、函式、類都是物件py_object,pyobject共有的資料結構是

ob_refcnt為該物件被引用的次數,為0時執行對應的析構;ob_type為該物件的型別(即type),type本身也是乙個pyobject。。。

2.可變變數和不可變變數

python中所有的=賦值都是傳遞的指標,因此可以認為python的函式都是實參傳遞,區別在於傳遞的引數是可變還是不可變。

不可變變數主要為數值和字串。python在堆中維護對應的列表,同值的不可變變數在記憶體指向同乙個位址,例如

a = 1

a += 1

當a = 1時,a指向記憶體中int(1)的位址,當執行a += 1時,實際執行的是a.__add__(self, 1),返回的是int(2)的位址。即不可變變數在值修改的時候並沒有修改對應物件的值,而是新生成乙個新值的物件返回。

而不可變數的位址則不會變化,對不可變變數進行修改則會真實修改記憶體中的值。

3.python執行時堆疊的變化

以最簡單的加法為例

a = 250

b = 250

def add(a, b):

c = a + b

return c

在c語言中呼叫該函式時,棧中儲存a、b、c、和呼叫前的bp值。當呼叫結束時c的值作為返回值傳遞給ax,再把bp值pop出來。這樣的函式在編譯的時候就已經能夠確定其占用的棧大小。

python和傳統的靜態語言不一樣的是,python的每乙個物件都可以拓展內部的值。例如已經例項化的乙個物件host_controller = controller(req, host_meta),可以在裡面增加屬性host_controller.test = ,之前也已經講到,python的每乙個變數都是指向棧中具體物件的指標,因此python的棧中都是*py_object的指標,而其具體指在heap中。

當執行add的時候,會在記憶體中留出a、b和c的*py_object的位址,即python函式在執行的時候棧中只儲存了對應的指標,實際的物件內容在堆中。

4.字串物件

python中,字串物件也是不可變,內容相同的字串指向記憶體中同一位址(unicode物件沒研究過,應該實現機制類似)。string物件除了維護char型別的陣列外,還有hash值,因此在編碼時盡量用host_id或者host_name作為索引,因為這兩個值在程式中大部分時候都存在在記憶體中,hash值不需要重複計算,直接以此為索引可以增加命中速度。

三、gil

前面提到,python的物件都在堆中,因而不像靜態語言一樣函式呼叫結束的時候直接移動bp和sp指標即可釋放記憶體。前面又提有每個python物件都有ob_refcnt值,這個值代表了物件的引用次數,每當賦值給乙個變數的時候ob_refcnt會自加1,當變數被釋放或被賦予其他物件指標時,ob_refcnt會自減1。當ob_refcnt為0時python會執行該物件的析構函式__del__,然後free掉對應記憶體。而且python物件都儲存在堆中,多執行緒都可以讀寫,無法保證ob_refcnt變化和析構之間的原子性,因此對執行緒加鎖。

協程巢狀協程

import asyncio import functools 第三層協程 async def test1 print 我是test1 await asyncio.sleep 1 print test1已經睡了1秒 await asyncio.sleep 3 print test1又睡了3秒 ret...

9 協程 協程理論

本節的主題是基於單執行緒來實現併發,即只用乙個主線程 很明顯可利用的cpu只有乙個 情況下實現併發,為此我們需要先回顧下併發的本質 切換 儲存狀態 ps 在介紹程序理論時,提及程序的三種執行狀態,而執行緒才是執行單位,所以也可以將上圖理解為執行緒的三種狀態cpu正在執行乙個任務,會在兩種情況下切走去...

python協程與非同步協程

在前面幾個部落格中我們一一對應解決了消費者消費的速度跟不上生產者,浪費我們大量的時間去等待的問題,在這裡,針對業務邏輯比較耗時間的問題,我們還有除了多程序之外更優的解決方式,那就是協程和非同步協程。在引入這個概念之前我們先看 看這個圖 從這個我們可以看出來,假如來了9個任務,即使我們開了多程序,在業...