LoginSignup
25
16

More than 5 years have passed since last update.

Ruby Fiberの基礎まとめ

Last updated at Posted at 2015-11-25

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>'

参考

パーフェクトRuby

25
16
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
25
16