Ruby 1.9.2 リファレンスマニュアル > ライブラリ一覧 > 組み込みライブラリ > Fiberクラス

class Fiber

クラスの継承リスト: Fiber < Object < Kernel < BasicObject

Abstract

ノンプリエンプティブな軽量スレッド(以下ファイバーと呼ぶ)を提供します。 他の言語では coroutine あるいは semicoroutine と呼ばれることもあります。 Thread と違いユーザレベルスレッドとして実装されています。

Thread クラスが表すスレッドと違い、明示的に指定しない限り ファイバーのコンテキストは切り替わりません。 またファイバーは親子関係を持ちます。Fiber#resume を呼んだファイバーが親になり 呼ばれたファイバーが子になります。親子関係を壊すような遷移(例えば 自分の親の親のファイバーへ切り替えるような処理)はできません。 例外 FiberError が発生します。 できることは

の二通りです。この親子関係は動的なものであり 親ファイバーへコンテキストを切り替えた時点で解消されます。

なお標準添付ライブラリ fiber を require することにより、 コンテキストの切り替えに制限のない Fiber#transfer が使えるようになります。 任意のファイバーにコンテキストを切り替えることができます。

ファイバーが終了するとその親にコンテキストが切り替わります。

例外

ファイバー実行中に例外が発生した場合、親ファイバーに例外が伝播します。

例:

f = Fiber.new do
  raise StandardError, "hoge"
end

begin
  f.resume     # ここでも StandardError が発生する。
rescue => e
  p e.message  #=> "hoge"
end

ショートチュートリアル

ファイバーは処理のあるポイントで他のルーチンにコンテキストを切り替え、またそのポイントから再開する という目的のために使います。 Fiber.new により与えられたブロックとともにファイバーを生成します。 生成したファイバーに対して Fiber#resume を呼ぶことによりコンテキストを切り替えます。 子ファイバーのブロック中で Fiber.yield を呼ぶと親にコンテキストを切り替えます。 Fiber.yield の引数が、親での Fiber#resume の返り値になります。

f = Fiber.new do
  n = 0
  loop do
    Fiber.yield(n)
    n += 1
  end
end

5.times do
 p f.resume
end

#=> 0
    1
    2
    3
    4

以下は内部イテレータを外部イテレータに変換する例です。 実際 Enumerator は Fiber を用いて実装されています。

def enum2gen(enum)
  Fiber.new do
    enum.each{|i|
      Fiber.yield(i)
    }
  end
end

g = enum2gen(1..100)

p g.resume  #=> 1
p g.resume  #=> 2
p g.resume  #=> 3

注意

Thread クラスが表すスレッド間をまたがるファイバーの切り替えはできません。 例外 FiberError が発生します。

f = nil
Thread.new do
  f = Fiber.new{}
end.join
f.resume
#=> t.rb:5:in `resume': fiber called across threads (FiberError)
       from t.rb:5:in `<main>'

特異メソッド

new {|obj| ... } -> Fiber

与えられたブロックとともにファイバーを生成して返します。 ブロックは Fiber#resume に与えられた引数をその引数として実行されます。

ブロックが終了した場合は親にコンテキストが切り替わります。 その時ブロックの評価値が返されます。

a = nil
f = Fiber.new do |obj|
  a = obj
  :hoge
end

b = f.resume(:foo)
p a  #=> :foo
p b  #=> :hoge
yield(*arg = nil) -> object

現在のファイバーの親にコンテキストを切り替えます。

コンテキストの切り替えの際に Fiber#resume に与えられた引数を yield メソッドは返します。

[PARAM] arg:
現在のファイバーの親に渡したいオブジェクトを指定します。
[EXCEPTION] FiberError:
Fiber でのルートファイバーで呼ばれた場合に発生します。

例:

a = nil
f = Fiber.new do
  a = Fiber.yield()
end

f.resume()
f.resume(:foo)

p a  #=> :foo

インスタンスメソッド

resume(*arg = nil) -> object

自身が表すファイバーへコンテキストを切り替えます。 自身は resume を呼んだファイバーの子となります。

コンテキストの切り替えの際に Fiber.yield に与えられた引数を resume メソッドは返します。

[PARAM] arg:
self が表すファイバーに渡したいオブジェクトを指定します。
[EXCEPTION] FiberError:
自身が既に終了している場合、コンテキストの切替が Thread クラスが表すスレッド間をまたがる場合、自身が resume を 呼んだファイバーの親かその祖先である場合に発生します。

例:

f = Fiber.new do
  Fiber.yield(:hoge)
end

a = f.resume()
f.resume()

p b  #=> :hoge

追加されるメソッド

alive? -> bool [added by fiber]

ファイバーが「生きている」時、真を返します。

このメソッドが真を返すのは以下の場合です。

  • まだ Fiber#resume されていない
  • ブロック内の評価が終了していない (Fiber.yield が呼ばれていない)

例:

fr = Fiber.new{
  Fiber.yield
  "a"
}

p fr.alive? # => true
fr.resume   # Fiber.yieldで戻ってくる
p fr.alive? # => true
fr.resume   # ブロック内の評価を終えて戻ってくる
p fr.alive? # => false
current -> Fiber [added by fiber]

このメソッドが評価されたコンテキストにおける Fiber のインスタンスを返します。

例:

fr = Fiber.new do
  Fiber.current
end

fb = fr.resume
p fb.equal?(fr) # => true

p Fiber.current # => #<Fiber:0x91345e4>
p Fiber.current # => #<Fiber:0x91345e4>
transfer(*args) -> object [added by fiber]

自身が表すファイバーへコンテキストを切り替えます。

自身は Fiber#resume を呼んだファイバーの子となります。 Fiber#resume との違いは、ファイバーが終了したときや Fiber.yield が呼ばれたときは、 ファイバーの親へ戻らずにメインファイバーへ戻ります。

[PARAM] args:
メインファイバーから呼び出した Fiber#resume メソッドの返り値として渡したいオブジェクトを指定します。
[RETURN]
コンテキスト切り替えの際に、Fiber#resume メソッドに与えられた引数を返します。
[EXCEPTION] FiberError:
自身が既に終了している場合、コンテキストの切り替えが Thread クラスが表すスレッド間をまたがる場合、 Fiber#resume を呼んだファイバーがその親か先祖である場合に発生します。

例:

require 'fiber'

fr1 = Fiber.new do |v|
  :fugafuga
end

fr2 = Fiber.new do |v|
  fr1.transfer
  :fuga
end

fr3 = Fiber.new do |v|
  fr2.resume
  :hoge
end

p fr3.resume # => :fugafuga

Methods

Classes