摘要:特別是最火的協程框架也無法保存狀態,讓人非常惋惜。但是因為棧的本身無法持久化,所以也就無法持久化。其難度在于,假設整個要持久化的調用棧全部都是內的,比如純的。采取的是暴力地把整個棧區域拷貝到上的方式來保存其狀態。
python主流的協程實現有五種:
cPython的generator
cPython的greenlet
cPython的fibers
stackless python
pypy
除了stackless python和pypy的實現版本之外,其余的協程都實現都無法保存狀態。特別是最火的協程框架greenlet也無法保存狀態,讓人非常惋惜。使用stackless python在公司內部的項目里實現了在一臺服務器上跑的任務,中斷之后在另外一臺服務器上繼續執行,非常的awsome!
為什么greenlet的狀態無法保存,而其前身stackless python就可以?我們可以先來看一下Java里的協程是如何實現的(http://www.slideshare.net/srirammalhar/communicating-state-machines)
這個是kilim的實現方式。大概的意思就是把棧的每一層frame的局部變量額外保存到協程對應的context里。因為局部變量不過是對heap上對象的引用,所以這些拷貝成本也很低。當前的執行位置使用label的方式標記,用goto可以恢復到之前的執行位置。
最后一個協程的狀態要保存的話,就是一個純Java的context對象做一下序列化就可以了,并不是非常困難。
這種實現協程的方式用stackless python的術語來說就是soft switching。而且kilim的實現也不是最優的,因為其記錄的context信息在jvm的內部實現的棧上也有一份。只是jvm沒有把這樣的執行狀態以api的形式暴露出來,使得開發者不得不額外耗費cpu指令來double booking的記錄一份同樣的信息到另外一個地方。stackless python和pypy就是把自己的內部的棧(形式和kilim的context的實現也差不多,就是frame的狀態列表)實現成為可持久化的,所以他們的協程就是可以持久化的。
cPython的generator原理上也是soft switching。其generator內部保存的就是一個python棧的frame(目測3.3的yield from就是保存了一個frame的列表)。但是因為python棧的frame本身無法持久化,所以generator也就無法持久化。理論上來說通過bytecode級別的修改目標函數,可以和kilim一樣在python vm之上利用其JMP_ABSOLUTE指令也是可以實現可持久化的協程的(https://docs.python.org/2/library/dis.html#bytecodes http://code.activestate.com/recipes/576944-the-goto-decorator/)。到目前為止,可能是因為stackless python等實現的存在,沒有在python中修改bytecode的剛需,也就沒有kilim這樣的勇夫出現。
另外一類的協程實現是hard switching。所謂hard就是done in hard way的意思。其難度在于,soft switching假設整個要持久化的調用棧全部都是vm內的,比如純python的。但是如果存在python => c擴展 => python這樣的嵌套形式,那么調用棧上就會有c的棧,而不僅僅是python的棧。對于c的棧,我們是無法知道哪些區域是一個變量,而這個變量是一個普通的值,還是一個指向heap上某個位置的指針的。hard switching采取的是暴力地把整個棧區域拷貝到heap上的方式來保存其狀態。對于只是switching來說,這樣做是足夠的了的。即便棧上放的是一個指針,要switch回來的時候把這個指針重新放回棧上就可以了。但是這種實現對于持久化來說是不夠的,僅僅保存一個指針,而不保存指針指向的值的話,恢復出來的指針就會指向天曉得的某個區域了。
借一個圖(http://www.slideshare.net/saghul/stack-switching-for-fun-and-profit):
hard switching有三份實現
stackless python 2.0 是純hard switching的,3.0改成了soft switching和hard switching混合
greenlet 是Armin Rigo經過stackless作者啟發,然后寫的一個新的真正通用的hard switching的實現
pypy 里的 stacklet 是Armin Rigo新寫的一個實現,據說更牛x
因為pypy的stacklet庫是一個獨立的實現,而且非常通用
fibers用其來實現了一個類似greenlet的協程框架(Armin Rigo大牛可能不屑于做這樣簡單的重復包裝吧)
libgevent 用stacklet和pyuv實現了一個c版本的協程網絡編程框架
不管stacklet怎么牛,其本質也是一個hard switching的實現,所以也無法實現狀態的保存(本質就是一個調用棧內容的深拷貝)。所以python搞流程引擎,stackless還是不二之選。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/37399.html
摘要:常規版本的的是不可以被持久化保存的。在流程被阻塞的時候比如需要審批老板不在把協程持久化成入庫,等流程不再阻塞的時候把協程重新從數據庫里拉起來繼續執行。 常規版本的Python的generator是不可以被持久化保存的。但是stackless和pypy這兩個修改版本的Python解釋器可以。下面這段代碼演示了如何把一個執行中的函數持久化保存,然后過段時間再把函數從上次執行到的地方原樣拉起...
摘要:隨著我們對于效率的追求不斷提高,基于單線程來實現并發又成為一個新的課題,即只用一個主線程很明顯可利用的只有一個情況下實現并發。作為的補充可以檢測操作,在遇到操作的情況下才發生切換協程介紹協程是單線程下的并發,又稱微線程,纖程。 引子 之前我們學習了線程、進程的概念,了解了在操作系統中進程是資源分配的最小單位,線程是CPU調度的最小單位。按道理來說我們已經算是把cpu的利用率提高很多了。...
摘要:事件循環是異步編程的底層基石。對事件集合進行輪詢,調用回調函數等一輪事件循環結束,循環往復。協程直接利用代碼的執行位置來表示狀態,而回調則是維護了一堆數據結構來處理狀態。時代的協程技術主要是,另一個比較小眾。 Coding Crush Python開發工程師 主要負責豈安科技業務風險情報系統redq。 引言 1.1. 存儲器山 存儲器山是 Randal Bryant 在《深入...
摘要:標準的異常處理是這樣的這段代碼會打印出而不會打印出,因為異常會中斷當前流程,跳轉到部分去繼續執行。這種行為類似里的。如何實現的其實原理上很簡單。的時候把當前協程的狀態保存起來,如果決定要,就把協程的時刻的狀態重新恢復然后從那個點繼續執行。 標準的異常處理是這樣的 try: print(hello) raise Exception() print(!!!) ex...
摘要:比如里可以直接把執行權交給,而完全不知情。雖然不能和多線程相比,但是效果是類似的。對于多線程的代碼,是任何一行代碼都可能與其他線程并行。加上協程之間有共享狀態的話,一定程度上會產生類似多線程的并發讀寫狀態的。 前面講generator是顯式的協程的時候缺一個例子,現在補上 def parent_generator(): print(hello) yield from ...
閱讀 2381·2021-10-09 09:41
閱讀 3172·2021-09-26 09:46
閱讀 835·2021-09-03 10:34
閱讀 3151·2021-08-11 11:22
閱讀 3364·2019-08-30 14:12
閱讀 711·2019-08-26 11:34
閱讀 3344·2019-08-26 11:00
閱讀 1749·2019-08-26 10:26