Fiberとは
- Ruby 1.9から登場 class Fiber (Ruby 2.1.0 リファレンス)
- 「ある処理を途中まで実行して、その後任意のタイミングで前回の続きから処理を行う」ことが可能になる
- スレッドはプリエンプティブ、Fiberはノンプリエンプティブ
- プリエンプティブ: 各スレッドの実行はスケジューラによって自動的行われている
- ノンプリエンプティブ: こちらで明示的に実行を指示
使い方
基本的な使い方
yieldとresume(fiber_1.rb)
#!/usr/bin/ruby
# vim:set fileencoding=utf-8
fiber = Fiber.new {
puts 'Hello, Fiber'
Fiber.yield # 何回でもはさめる
puts 'Hello (again)'
}
fiber.resume # yeildの時点まで実行
fiber.resume # その次
fiber.resume # その次
Result(fiber_1.rb)
% ruby fiber_1.rb
Hello, Fiber
Hello (again)
fiber_1.rb:14:in `resume': dead fiber called (FiberError)
from fiber_1.rb:14:in `<main>'
引数と戻り値の使い方
引数と戻り値(fiber_2.rb)
#!/usr/bin/ruby
# vim:set fileencoding=utf-8
fiber = Fiber.new {|first|
puts first
second = Fiber.yield('goodbye')
puts second
'goodbye (again)'
}
puts fiber.resume('hello')
puts fiber.resume('hello(again)')
Result(fiber_2.rb)
% ruby fiber_2.rb
hello
goodbye
hello(again)
goodbye (again)
ジェネレータ
ジェネレータは、プログラムにおいて、数列の各要素の値などを次々と生成(ジェネレート)し他の手続きに渡す、という機能を持っている手続きである。 参照:wikipedia
Fiberを使うと「膨大な計算結果の配列を一度に構築するのではなく、部分的な結果だけを計算して返し、自家また続きから計算」できるサブルーチンを作ることができます。
つまり一度に大きな配列を作成しない → メモリ効率良い!
フィボナッチ数を返却するジェネレータの例
フィボナッチ数を返すジェネレータ(fiber_fibonach.rb)
fib = Fiber.new {
a, b = 0, 1
loop do
a, b = b, a + b
Fiber.yield(a)
end
}
p 10.times.map{ fib.resume }
puts fib.resume
puts fib.resume
Result(fiber_fibonach.rb)
% ruby fiber_fibonach.rb
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
89
144
スレッドと組み合わせた場合の制限
Fiberの切り替えはスレッドを超えては実行できない。
他のスレッドのファイバをresumeした際や、Fiberの中のスレッドからFiber.yieldした際はFiberErrorが発生する。
Fiber&Thread(1)
fiber = Fiber.new {
puts 'OH MY GOD'
}
Thread.fork {
fiber.resume
}.join
Result(1)
% ruby fiber_other_thread.rb
fiber_other_thread.rb:8:in `resume': fiber called across threads (FiberError)
from fiber_other_thread.rb:8:in `block in <main>'
Fiber&Thread(2)
# Fiber.yield
fiber = Fiber.new {
Thread.fork {
Fiber.yield
}.join
}
fiber.resume
Result(2)
% ruby fiber_other_thread.rb
fiber_other_thread.rb:5:in `yield': can't yield from root fiber (FiberError)
from fiber_other_thread.rb:5:in `block (2 levels) in <main>'