莊周夢蝶

          生活、程序、未來
             :: 首頁 ::  ::  :: 聚合  :: 管理

          Ruby Fiber指南(一)基礎(chǔ)

          Posted on 2010-03-11 12:53 dennis 閱讀(8653) 評論(2)  編輯  收藏 所屬分類: 動態(tài)語言my open-source

              Ruby Fiber指南(一)基礎(chǔ)
              Ruby Fiber指南(二)參數(shù)傳遞
              Ruby Fiber指南(三)過濾器
              Ruby Fiber指南(四)迭代器
              Ruby Actor指南(五)實現(xiàn)Actor
             
              這是一個Ruby Fiber的教程,基本是按照《Programming in lua》中講述協(xié)程章節(jié)的順序來介紹Ruby Fiber的,初步分為5節(jié):基礎(chǔ)、參數(shù)傳遞、過濾器、迭代器、應(yīng)用。這是第一節(jié),介紹下Ruby Fiber的基礎(chǔ)知識。

              Ruby 1.9引入了Fiber,通常稱為纖程,事實上跟傳統(tǒng)的coroutine——協(xié)程是一個概念,一種非搶占式的多線程模型。所謂非搶占式就是當(dāng)一個協(xié)程運行的時候,你不能在外部終止它,而只能等待這個協(xié)程主動(一般是yield)讓出執(zhí)行權(quán)給其他協(xié)程,通過協(xié)作來達(dá)到多任務(wù)并發(fā)的目的。協(xié)程的優(yōu)點在于由于全部都是用戶空間內(nèi)的操作,因此它是非常輕量級的,占用的資源很小,并且context的切換效率也非常高效(可以看看這個測試),在編程模型上能簡化對阻塞操作或者異步調(diào)用的使用,使得涉及到此類操作的代碼變的非常直觀和優(yōu)雅;缺點在于容錯和健壯性上需要做更多工作,如果某個協(xié)程阻塞了,可能導(dǎo)致整個系統(tǒng)掛住,無法充分利用多核優(yōu)勢,有一定的學(xué)習(xí)使用曲線。
             上面都是場面話,先看看代碼怎么寫吧,比如我們寫一個打印hello的協(xié)程:
           1 require 'fiber'
           2 f=Fiber.new do
           3   p "hello"
           4 end
           5 
           6 p f.alive?
           7 f.resume
           8 p f.alive?
           9 
          10 f.resume
          11
              附注:這里的代碼都在ruby1.9.1-p378測試通過。

               第一行先引入fiber庫,事實上fiber庫并不是必須的,這里是為了調(diào)用Fiber#alive?方法才引入。然后通過Fiber#new創(chuàng)建一個Fiber,F(xiàn)iber#new接受一個block,block里就是這個Fiber將要執(zhí)行的任務(wù)。Fiber#alive?用來判斷Fiber是否存活,一個Fiber有三種狀態(tài):Created、Running、Terminated,分別表示創(chuàng)建完成、執(zhí)行、終止,處于Created或者Running狀態(tài)的時候Fiber#alive?都返回true。啟動Fiber是通過Fiber#resume方法,這個Fiber將進(jìn)入Running狀態(tài),打印"hello"并終止。當(dāng)一個Fiber終止后,如果你再次調(diào)用resume將拋出異常,告訴你這個Fiber已經(jīng)壽終正寢了。因此上面的程序輸出是:
          0
          "hello"
          false
          fiber1.rb:
          10:in `resume': dead fiber called (FiberError)
              from fiber1.rb:10:in `<main>'

               眼尖的已經(jīng)注意到了,這里alive?返回是0,而不是true,這是1.9.1這個版本的一個BUG,1.9.2返回的就是true。不過在Ruby里,除了nil和false,其他都是true。

              剛才提到,我們?yōu)榱苏{(diào)用Fiber#alive?而引入了fiber庫,F(xiàn)iber其實是內(nèi)置于語言的,并不需要引入額外的庫,fiber庫對Fiber的功能做了增強,具體可以先看看它的文檔,主要是引入了幾個方法:Fiber#current返回當(dāng)前協(xié)程,F(xiàn)iber#alive?判斷Fiber是否存活,最重要的是Fiber#transfer方法,這個方法使得Ruby的Fiber支持所謂全對稱協(xié)程(symmetric coroutines),默認(rèn)的resume/yield(yield后面會看到)是半對稱的協(xié)程(asymmetric coroutines),這兩種模型的區(qū)別在于掛起一個正在執(zhí)行的協(xié)同函數(shù)”與“使一個被掛起的協(xié)同再次執(zhí)行的函數(shù)”是不是同一個。在這里就是Fiber#transfer一個方法做了resume/yield兩個方法所做的事情。全對稱協(xié)程就可以從一個協(xié)程切換到任意其他協(xié)程,而半對稱則要通過調(diào)用者來中轉(zhuǎn)。但是Ruby Fiber的調(diào)用不能跨線程(thread,注意跟fiber區(qū)分),只能在同一個thread內(nèi)進(jìn)行切換,看下面代碼:
          1 = nil
          2 Thread.new do
          3   f = Fiber.new{}
          4 end.join
          5 f.resume

          f在線程內(nèi)創(chuàng)建,在線程外調(diào)用,這樣的調(diào)用在Ruby 1.9里是不允許的,執(zhí)行的結(jié)果將拋出異常
          fiber_thread.rb:5:in `resume': fiber called across threads (FiberError)
              from fiber_thread.rb:5:in `<main>'

              剛才我們僅僅使用了resume,那么yield是干什么的呢?resume是使一個掛起的協(xié)程執(zhí)行,那么yield就是讓一個正在執(zhí)行的Fiber掛起并將執(zhí)行權(quán)交給它的調(diào)用者,yield只能在某個Fiber任務(wù)內(nèi)調(diào)用,不能在root Fiber調(diào)用,程序的主進(jìn)程就是一個root fiber,如果你在root fiber執(zhí)行一個Fiber.yield,也將拋出異常:
           Fiber.yield
          FiberError: can
          't yield from root fiber
            
              看一個resume結(jié)合yield的例子:
           1 f=Fiber.new do
           2   p 1
           3   Fiber.yield
           4   p 2
           5   Fiber.yield
           6   p 3
           7 end
           8 
           9 f.resume # =>打印1
          10 f.resume # => 打印2
          11 f.resume # =>打印3

             f是一個Fiber,它的任務(wù)就是打印1,2,3,第一次調(diào)用resume時,f在打印1之后調(diào)用了Fiber.yield,f將讓出執(zhí)行權(quán)給它的調(diào)用者(這里就是root fiber)并掛起,然后root fiber再次調(diào)用f.resume,那么將從上次掛起的地方繼續(xù)執(zhí)行——打印2,又調(diào)用Fiber.yield再次掛起,最后一次f.resume執(zhí)行后續(xù)的打印任務(wù)并終止f。

              Fiber#yield跟語言中的yield關(guān)鍵字是不同的,block中的yield也有“讓出”的意思,但是這是在同一個context里,而Fiber#yield讓出就切換到另一個context去了,這是完全不同的。block的yield其實是匿名函數(shù)的語法糖衣,它是切換context的,跟Fiber不同的是,它不保留上一次調(diào)用的context,這個可以通過一個例子來區(qū)分:
          1 def test
          2    yield
          3    yield
          4    yield
          5 end
          6 test{x ||= 0; puts x+= 1}
          7 
          這里的test方法接受一個block,三次調(diào)用yield讓block執(zhí)行,block里先是初始化x=0,然后每次調(diào)用加1,你期望打印什么?
          答案是:
          1
          1
          1
          這個結(jié)果剛好證明了yield是不保留上一次調(diào)用的context,每次x都是重新初始化為0并加上1,因此打印的都是1。讓我們使用Fiber寫同一個例子:
           1 fiber=Fiber.new do
           2    x||=0
           3    puts x+=1
           4    Fiber.yield
           5    puts x+=1
           6    Fiber.yield
           7    puts x+=1
           8    Fiber.yield
           9 end
          10 
          11 fiber.resume
          12 fiber.resume
          13 fiber.resume
          14 
          執(zhí)行的結(jié)果是:
          1
          2
          3
          這次能符合預(yù)期地打印1,2,3,說明Fiber的每次掛起都將當(dāng)前的context保存起來,留待下次resume的時候恢復(fù)執(zhí)行。因此關(guān)鍵字yield是無法實現(xiàn)Fiber的,fiber其實跟continuation相關(guān),在底層fiber跟callcc的實現(xiàn)是一致的(cont.c)。

              Fiber#current返回當(dāng)前執(zhí)行的fiber,如果你在root fiber中調(diào)用Fiber.current返回的就是當(dāng)前的root fiber,一個小例子:
          1 require 'fiber'
          2 f=Fiber.new do
          3    p Fiber.current
          4 end
          5 
          6 p Fiber.current
          7 f.resume

          這是一次輸出:
          #<Fiber:0x9bf89f4>
          #
          <Fiber:0x9bf8a2c>
          表明root fiber跟f是兩個不同的Fiber。
              
               基礎(chǔ)的東西基本講完了,最后看看Fiber#transfer的簡單例子,兩個協(xié)程協(xié)作來打印“hello world”:
           1 require 'fiber'
           2 
           3 f1=Fiber.new do |other|
           4     print "hello"
           5     other.transfer
           6 end
           7 
           8 f2=Fiber.new do
           9     print " world\n"
          10 end
          11 
          12 f1.resume(f2)

          通過這個例子還可以學(xué)到一點,resume可以傳遞參數(shù),參數(shù)將作為Fiber的block的參數(shù),參數(shù)傳遞將是下一節(jié)的主題。


             

          評論

          # re: Ruby Fiber指南(一)基礎(chǔ)  回復(fù)  更多評論   

          2013-12-24 14:00 by daijie
          Fibur = Thread ?

          # re: Ruby Fiber指南(一)基礎(chǔ)  回復(fù)  更多評論   

          2013-12-24 14:01 by daijie
          sorry 沒看清,please remove this comment.
          主站蜘蛛池模板: 枣强县| 湖北省| 宣城市| 方正县| 和林格尔县| 宣汉县| 隆安县| 安福县| 玉溪市| 龙泉市| 阳山县| 西城区| 黑龙江省| 宁阳县| 商河县| 禄丰县| 江安县| 黄陵县| 普宁市| 周口市| 武宁县| 锡林浩特市| 隆尧县| 红河县| 金乡县| 东港市| 萨迦县| 枝江市| 巴林右旗| 延川县| 太湖县| 雅江县| 大同市| 龙州县| 哈尔滨市| 临湘市| 灵武市| 武平县| 盐亭县| 榆林市| 民勤县|