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