このマニュアルは既にメンテナンスが終了したバージョンの Ruby を対象としています。 最新版のマニュアルへ

Ruby 3.0 リファレンスマニュアル

class Fiber

[edit]

要約

ノンプリエンプティブな軽量スレッド(以下ファイバーと呼ぶ)を提供します。他の言語では 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>'

ノンブロッキングファイバーとスケジューラ

Ruby 3.0 から、ファイバーはブロッキングとノンブロッキングのどちらかの実行コンテキストを持ちます。 Fiber.new は既定でノンブロッキングなファイバーを生成します。 blocking: true を指定するとブロッキングなファイバーになります。

ノンブロッキングファイバーの中でブロックしうる操作を行うと、その操作はスケジューラに委譲されます。ブロックしうる操作とは、IO 待ちやスリープなどです。スケジューラは Fiber.set_scheduler でスレッドごとに設定します。

スケジューラを設定していない場合、ノンブロッキングファイバーはブロッキングファイバーと同じ動作になります。つまりノンブロッキングファイバーであること自体は実行の挙動を変えません。

スケジューラは Ruby 本体では提供されていません。フックメソッドを実装したオブジェクトを利用者が用意します。実装すべきメソッドは Ruby 本体の Fiber::Scheduler のドキュメントで説明されています。

現在の実行コンテキストがどちらであるかは Fiber.blocking? で調べられます。また Fiber.schedule を使うと、スケジューラ経由でノンブロッキングファイバーを生成できます。

目次

特異メソッド
インスタンスメソッド
追加されるメソッド

特異メソッド

blocking? -> false | 1Ruby 3.0 から[permalink][rdoc][edit]

現在の実行コンテキストがブロッキングである場合に 1 を返します。ノンブロッキングである場合は false を返します。

将来のバージョンで、1 以外のブロッキングレベルを表す値が返るようになる可能性があります。

p Fiber.blocking?                                      # => 1
p Fiber.new { Fiber.blocking? }.resume                 # => false
p Fiber.new(blocking: true) { Fiber.blocking? }.resume # => 1

[SEE_ALSO] Fiber#blocking?, Fiber/ノンブロッキングファイバーとスケジューラ

new(blocking: false) {|obj| ... } -> FiberRuby 1.9.3 から[permalink][rdoc][edit]

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

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

[PARAM] blocking:
偽を指定するとノンブロッキングなファイバーを生成します。真を指定するとブロッキングなファイバーを生成します。詳しくは Fiber/ノンブロッキングファイバーとスケジューラ を参照してください。
例:
a = nil
f = Fiber.new do |obj|
  a = obj
  :hoge
end
  
b = f.resume(:foo)
p a  #=> :foo
p b  #=> :hoge
schedule(*args) {|*args| ... } -> FiberRuby 3.0 から[permalink][rdoc][edit]

現在のスレッドに設定されているスケジューラを使って、ブロックをノンブロッキングなファイバーで実行します。

ファイバーの生成はスケジューラのフックメソッドに委譲されます。そのため、ブロックがただちに実行されるかどうかはスケジューラの実装に依存します。

[PARAM] args:
ブロックの引数として渡されます。
[RETURN]
生成されたファイバーを返します。
[EXCEPTION] RuntimeError:
スケジューラが設定されていない場合に発生します。
例: スケジューラが設定されていない場合
Fiber.schedule { }  # ~> RuntimeError: No scheduler is available!

[SEE_ALSO] Fiber.set_scheduler, Fiber/ノンブロッキングファイバーとスケジューラ

scheduler -> object | nilRuby 3.0 から[permalink][rdoc][edit]

現在のスレッドに設定されているスケジューラを返します。設定されていない場合は nil を返します。

p Fiber.scheduler # => nil

[SEE_ALSO] Fiber.set_scheduler

set_scheduler(scheduler) -> objectRuby 3.0 から[permalink][rdoc][edit]

現在のスレッドにスケジューラを設定します。

スケジューラを設定すると、ノンブロッキングファイバーの中でブロックしうる操作を行った際に、スケジューラのフックメソッドが呼ばれるようになります。またスレッドの終了時にスケジューラの close メソッドが呼ばれ、終了していないファイバーの後始末ができるようになっています。

[PARAM] scheduler:
スケジューラとして振る舞うオブジェクトを指定します。 nil を指定するとスケジューラを解除します。
[RETURN]
scheduler をそのまま返します。
[EXCEPTION] ArgumentError:
scheduler が必要なフックメソッドを実装していない場合に発生します。
Fiber.set_scheduler(Object.new) # ~> ArgumentError: Scheduler must implement #block

[SEE_ALSO] Fiber.scheduler, Fiber.schedule, Fiber/ノンブロッキングファイバーとスケジューラ

yield(*arg = nil) -> objectRuby 1.9.3 から[permalink][rdoc][edit]

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

コンテキストの切り替えの際に 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

インスタンスメソッド

backtrace -> [String]Ruby 3.0 から[permalink][rdoc][edit]
backtrace(start) -> [String]
backtrace(start, length) -> [String]
backtrace(range) -> [String]

self が表すファイバーの現在の実行スタックを返します。

引数を指定すると、返すスタックの範囲を指定できます。引数の意味は Kernel.#caller と同じです。

ファイバーの実行が開始される前と、終了した後は空の配列を返します。

[PARAM] start:
開始フレームの位置を数値で指定します。
[PARAM] length:
取得するフレームの個数を指定します。
[PARAM] range:
取得したいフレームの範囲を Range で指定します。
def level3 = Fiber.yield
def level2 = level3
def level1 = level2

f = Fiber.new { level1 }

# 開始前は空
p f.backtrace # => []

f.resume

p f.backtrace
# => ["t.rb:1:in `yield'", "t.rb:1:in `level3'", "t.rb:2:in `level2'",
#     "t.rb:3:in `level1'", "t.rb:5:in `block in <main>'"]
p f.backtrace(1, 2)
# => ["t.rb:1:in `level3'", "t.rb:2:in `level2'"]

f.resume

# 終了後も空
p f.backtrace # => []

[SEE_ALSO] Fiber#backtrace_locations, Kernel.#caller

backtrace_locations -> [Thread::Backtrace::Location]Ruby 3.0 から[permalink][rdoc][edit]
backtrace_locations(start) -> [Thread::Backtrace::Location]
backtrace_locations(start, length) -> [Thread::Backtrace::Location]
backtrace_locations(range) -> [Thread::Backtrace::Location]

Fiber#backtrace と同じですが、実行スタックの各行を Thread::Backtrace::Location の配列で返します。

引数の意味は Fiber#backtrace と同じです。

f = Fiber.new { Fiber.yield }
f.resume

loc = f.backtrace_locations.first
p loc.class  # => Thread::Backtrace::Location
p loc.lineno # => 1

[SEE_ALSO] Fiber#backtrace, Kernel.#caller_locations

blocking? -> boolRuby 3.0 から[permalink][rdoc][edit]

self がブロッキングなファイバーである場合に true を返します。ノンブロッキングである場合は false を返します。

Fiber.newblocking: true を指定して生成したファイバーがブロッキングです。

p Fiber.new { }.blocking?                 # => false
p Fiber.new(blocking: true) { }.blocking? # => true

[SEE_ALSO] Fiber.blocking?, Fiber/ノンブロッキングファイバーとスケジューラ

raise -> objectRuby 2.7.0 から[permalink][rdoc][edit]
raise(message) -> object
raise(exception, message = nil, backtrace = nil) -> object

selfが表すファイバーが最後に Fiber.yield を呼んだ場所で例外を発生させます。

Fiber.yield が呼ばれていないかファイバーがすでに終了している場合、 FiberError が発生します。

引数を渡さない場合、RuntimeError が発生します。 message 引数を渡した場合、message 引数をメッセージとした RuntimeError が発生します。

その他のケースでは、最初の引数は Exception か Exception のインスタンスを返す exception メソッドを持ったオブジェクトである必要があります。この場合、2つ目の引数に例外のメッセージを渡せます。また3つ目の引数に例外発生時のスタックトレースを指定できます。

[PARAM] message:
例外のメッセージとなる文字列です。
[PARAM] exception:
発生させる例外です。
[PARAM] backtrace:
例外発生時のスタックトレースです。文字列の配列で指定します。
f = Fiber.new { Fiber.yield }
f.resume
f.raise "Error!" # => Error! (RuntimeError)
ファイバー内のイテレーションを終了させる例
f = Fiber.new do
  loop do
    Fiber.yield(:loop)
  end
  :exit
end

p f.resume              # => :loop
p f.raise StopIteration # => :exit
resume(*arg = nil) -> objectRuby 1.9.3 から[permalink][rdoc][edit]

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

ただし、Fiber#transfer を呼び出した後に resume を呼び出す事はできません。

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

f = Fiber.new do
  Fiber.yield(:hoge)
  :fuga
end
  
p f.resume() #=> :hoge
p f.resume() #=> :fuga
f.resume()   # ~> FiberError: attempt to resume a terminated fiber

追加されるメソッド

alive? -> boolRuby 1.9.3 から[permalink][rdoc][edit] [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 -> FiberRuby 1.9.3 から[permalink][rdoc][edit] [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) -> objectRuby 1.9.3 から[permalink][rdoc][edit] [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