<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    莊周夢蝶

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

    Ruby Fiber指南(一)基礎

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

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

        Ruby 1.9引入了Fiber,通常稱為纖程,事實上跟傳統的coroutine——協程是一個概念,一種非搶占式的多線程模型。所謂非搶占式就是當一個協程運行的時候,你不能在外部終止它,而只能等待這個協程主動(一般是yield)讓出執行權給其他協程,通過協作來達到多任務并發的目的。協程的優點在于由于全部都是用戶空間內的操作,因此它是非常輕量級的,占用的資源很小,并且context的切換效率也非常高效(可以看看這個測試),在編程模型上能簡化對阻塞操作或者異步調用的使用,使得涉及到此類操作的代碼變的非常直觀和優雅;缺點在于容錯和健壯性上需要做更多工作,如果某個協程阻塞了,可能導致整個系統掛住,無法充分利用多核優勢,有一定的學習使用曲線。
       上面都是場面話,先看看代碼怎么寫吧,比如我們寫一個打印hello的協程:
     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庫并不是必須的,這里是為了調用Fiber#alive?方法才引入。然后通過Fiber#new創建一個Fiber,Fiber#new接受一個block,block里就是這個Fiber將要執行的任務。Fiber#alive?用來判斷Fiber是否存活,一個Fiber有三種狀態:Created、Running、Terminated,分別表示創建完成、執行、終止,處于Created或者Running狀態的時候Fiber#alive?都返回true。啟動Fiber是通過Fiber#resume方法,這個Fiber將進入Running狀態,打印"hello"并終止。當一個Fiber終止后,如果你再次調用resume將拋出異常,告訴你這個Fiber已經壽終正寢了。因此上面的程序輸出是:
    0
    "hello"
    false
    fiber1.rb:
    10:in `resume': dead fiber called (FiberError)
        from fiber1.rb:10:in `<main>'

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

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

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

        剛才我們僅僅使用了resume,那么yield是干什么的呢?resume是使一個掛起的協程執行,那么yield就是讓一個正在執行的Fiber掛起并將執行權交給它的調用者,yield只能在某個Fiber任務內調用,不能在root Fiber調用,程序的主進程就是一個root fiber,如果你在root fiber執行一個Fiber.yield,也將拋出異常:
     Fiber.yield
    FiberError: can
    't yield from root fiber
      
        看一個resume結合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,它的任務就是打印1,2,3,第一次調用resume時,f在打印1之后調用了Fiber.yield,f將讓出執行權給它的調用者(這里就是root fiber)并掛起,然后root fiber再次調用f.resume,那么將從上次掛起的地方繼續執行——打印2,又調用Fiber.yield再次掛起,最后一次f.resume執行后續的打印任務并終止f。

        Fiber#yield跟語言中的yield關鍵字是不同的,block中的yield也有“讓出”的意思,但是這是在同一個context里,而Fiber#yield讓出就切換到另一個context去了,這是完全不同的。block的yield其實是匿名函數的語法糖衣,它是切換context的,跟Fiber不同的是,它不保留上一次調用的context,這個可以通過一個例子來區分:
    1 def test
    2    yield
    3    yield
    4    yield
    5 end
    6 test{x ||= 0; puts x+= 1}
    7 
    這里的test方法接受一個block,三次調用yield讓block執行,block里先是初始化x=0,然后每次調用加1,你期望打印什么?
    答案是:
    1
    1
    1
    這個結果剛好證明了yield是不保留上一次調用的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 
    執行的結果是:
    1
    2
    3
    這次能符合預期地打印1,2,3,說明Fiber的每次掛起都將當前的context保存起來,留待下次resume的時候恢復執行。因此關鍵字yield是無法實現Fiber的,fiber其實跟continuation相關,在底層fiber跟callcc的實現是一致的(cont.c)。

        Fiber#current返回當前執行的fiber,如果你在root fiber中調用Fiber.current返回的就是當前的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。
        
         基礎的東西基本講完了,最后看看Fiber#transfer的簡單例子,兩個協程協作來打印“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)

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


       

    評論

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

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

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

    2013-12-24 14:01 by daijie
    sorry 沒看清,please remove this comment.
    主站蜘蛛池模板: 久久国产精品免费专区| 美女视频黄a视频全免费网站一区 美女视频黄a视频全免费网站色 | 亚洲最大中文字幕| 青青操视频在线免费观看| 免费一级毛片清高播放| 亚洲色最新高清av网站| 亚洲毛片免费视频| 亚洲精品亚洲人成在线麻豆| 国产午夜精品免费一区二区三区| 国产精品V亚洲精品V日韩精品 | 亚洲午夜在线电影| 今天免费中文字幕视频| 亚洲va久久久噜噜噜久久天堂| 国产一级a毛一级a看免费视频| 亚洲一级片免费看| 中文字幕无码免费久久| 亚洲AV无码一区二区二三区入口| 国产麻豆一精品一AV一免费| 亚洲日韩aⅴ在线视频| 99爱在线观看免费完整版| 亚洲美女激情视频| 好爽又高潮了毛片免费下载 | 亚洲成a人片在线观看无码专区| 最好免费观看韩国+日本| 亚洲日韩精品国产一区二区三区 | 亚洲熟妇无码AV不卡在线播放| 国产免费卡一卡三卡乱码| 香港经典a毛片免费观看看| 狠狠综合久久综合88亚洲| 毛片免费全部播放无码 | free哆拍拍免费永久视频 | 亚洲精品无码久久千人斩| 91免费人成网站在线观看18| 色天使色婷婷在线影院亚洲| 亚洲色大成网站www永久一区| 99免费在线观看视频| 污污的视频在线免费观看| 亚洲春黄在线观看| 亚洲一区爱区精品无码| 日本不卡在线观看免费v| 久久久久成人精品免费播放动漫|