很長一段時間,我都很天真的認為python,特別是以gevent為代表的庫,才是協程的樂土。Java裡是沒法實現協程,更別說實現stackless python這樣可以pickle的協程的。Bong!我們真的是太井底之蛙了。
Java不但可以實現協程,而且還有很多個實現版本。不完全列表如下:
- PicoThreads
http://research.microsoft.com/en-us/um/people/abegel/cs262.pdf - RIFE
http://rifers.org/wiki/display/RIFE/Web%20continuations.html - Javaflow
http://commons.apache.org/sandbox/commons-javaflow/ - Matthias Mann's
http://hg.l33tlabs.org/Continuations/
http://docs.paralleluniverse.co/quasar/
http://indiespot.net/files/projects/continuationslib/
還有一個據(作者)說是最NB的kilim (https://github.com/kilim/kilim)
這些協程庫的實現方式都是類似的,都是透過jvm位元組碼生成達到pause/resume的目的。在這篇文章中,RIFE的作者很清楚地講明白了其實現方式:
http://www.artima.com/lejava/articles/continuations.html
Geert Bevin: At the byte-code level, when the method pops variables
from the stack, exactly the same operation will be performed on the
parallel stack. We just add a piece of byte code that mimics what goes
on exactly. Then, when a continuation has to be resumed, there's a bit
of added byte-code that interacts with the state of the stack, and
puts all the variables in the correct location so that they are
present when the executing thread passes that point. At this point,
it's as if nothing happened—execution resumed as if nothing happened.
The second problem is how to arrive at the point to resume a
continuation. You want to skip over code that you don't want to
execute. That's easily done in byte code, because you can maintain a
tableswitch in byte code that allows you to jump to a particular
location. You can create a unique label for each continuation point,
jump to that label in the switch table, and then you know exactly what
the stack for that label of variables was. You can restore that stack,
and continue executing from that point on.
kiliam的作者用程式碼解釋得更加清楚(http://www.malhar.net/sriram/kilim/thread_of_ones_own.pdf):
// original
void a() throws Pasuable {
x = ...
b(); // b is pausable
print (x);
}
經過程式碼增強之後是
// transformed code
void a(Fiber f) {
switch (f.pc) { // prelude
case 0: goto START;
case 1: goto CALL_B}
START:
x = ...
CALL_B: // pre_call
f.down()
b(f);
f.up() // post-call
switch (f.status) {
case NOT_PAUSING_NO_STATE:
goto RESUME
case NOT_PAUSING_HAS_STATE:
restore state
goto RESUME
case PAUSING_NO_STATE :
capture state, return
case PAUSING_HAS_STATE:
return
}
RESUME:
print (x);
}
因為這些框架都是以java物件的方式來儲存call stack state和programe counter的,所以要做到序列化儲存一個執行中的狀態(continuation)一點都不困難。RIFE在其web框架就狠狠地利用了clonable的狀態來實現複雜的wizard(回到任意時刻地過去重新執行之類的)。
看過了這些實現之後,我不禁覺得持久化一個continuation好像沒啥了不起的,為什麼會是stackless python的pypy兩家的獨門絕技呢?基於CPython的兩個協程實現,一個greenlet一個fibers是否可以實現狀態的持久化?狀態持久化能不能不用更高效的serializer來實現呢?