來看看kilim是如何實現標示出掛起點、識別出需要備份的呼叫棧、備份/恢復呼叫棧、任務的喚醒這些功能的:
1、標識掛起點,kilim框架通過
kilim.task.pause(kilim.pausereason, kilim.fiber)
介面掛起任務,對外也提供了
kilim.mailbox.get()
介面來實現掛起,只要把這兩個介面放到需要掛起任務的地方,kilim框架就會在執行期實現此處掛起功能了。在掛起點還做了另外一件很重要的事情,就是任務作為乙個listener,會監聽mailbox,當我們在mailbox中放入資料的時候,會觸發這個***,喚醒任務,更詳細在最後一步介紹。當然對於僅僅使用mailbox提供的api傳遞資料的使用者,可能沒有意識到掛起動作的存在。
2、識別出需要備份的呼叫棧,kilim通過在原始碼級別讓方法丟擲kilim.pausable異常來標示出這個方法的執行期棧幀需要備份恢復。這個pausable是個checkedexception,所以呼叫這個方法的整個靜態呼叫鏈上,都需要丟擲pausable,這個這個靜態鏈對應於執行期jvm的棧。當然,你可以把這個受查異常吃掉,導致分析路徑不完整,執行期出錯。
3、備份呼叫棧,即備份/恢復整個呼叫棧上的所有棧幀裡邊的區域性變數和操作棧。kilim通過編譯期修改位元組碼,織入備份和恢復棧幀資訊的位元組碼,而這些對原始碼級開發者幾乎是透明的。kilim會分析所有丟擲pausable異常的方法的位元組碼,通過控制流分析找到要織入**的位置,計算行號表、try/catch/finally塊位址資訊、區域性變數表、最大棧深、跳轉指令目標位置等資訊;通過資料流分析備份執行期操作棧上的資料,一些計算過程中的中間結果並沒有儲存在區域性變數表,所以不能通過備份區域性變數表實現,必須把執行期的中間結果也分析出來,備份下來,如
a + b + remote.get(…)
這裡a+b只在執行期產生,但是後邊的方法呼叫會導致任務掛起,所以需要備份a+b的值。由於在第2步的時候所有整個呼叫鏈上所有方法都丟擲了pausable方法,所以織入的時候,整個呼叫鏈上所有方法都被織入了備份/恢復**,執行期整個操作棧上的棧幀都可以從掛起點開始被備份。
4、喚醒任務、恢復呼叫棧,在第三步我們已經把任務掛起了,但是在什麼時候可以恢復執行呢?第一步在掛起任務的時候在mailbox上註冊了***,等有訊息回來放入mailbox的時候,放訊息的那個執行緒會喚醒任務,任務會進入可執行任務佇列,等待工作執行緒來排程執行。如果等待執行緒佇列有執行緒在等待任務,就會喚醒乙個等待執行緒。被喚醒的工作執行緒會從可執行任務佇列中那任務來執行,重複以上過程直到整個任務完成。
gevent實現協程
1 yield實現 import time def task 1 while true print 1 time.sleep 0.1 yield def task 2 while true print 2 time.sleep 0.1 yield def main t1 task 1 建立迭代器 t...
coroutine php PHP 協程實現
多程序 執行緒 最早的伺服器端程式都是通過多程序 多執行緒來解決併發io的問題。程序模型出現的最早,從unix 系統誕生就開始有了程序的概念。最早的伺服器端程式一般都是 accept 乙個客戶端連線就建立乙個程序,然後子程序進入迴圈同步阻塞地與客戶端連線進行互動,收發處理資料。多執行緒模式出現要晚一...
coroutine php PHP 協程實現
多程序 執行緒 最早的伺服器端程式都是通過多程序 多執行緒來解決併發io的問題。程序模型出現的最早,從unix 系統誕生就開始有了程序的概念。最早的伺服器端程式一般都是 accept 乙個客戶端連線就建立乙個程序,然後子程序進入迴圈同步阻塞地與客戶端連線進行互動,收發處理資料。多執行緒模式出現要晚一...