Fiberの基本的な使い方
それでは、Fiberクラスの挙動を観察してみてみましょう。Fiberは標準クラスなのでrequireは必要ありません。
Fiber.new
するときには、Threadと同じくブロックを渡す必要があります。作成したFiberインスタンスはresume
メソッドを持ち、これを呼び出すとブロックが実行されます。
ただ「ブロックのコードを任意の場所で呼び出せる」だけではProcと変わりません。Fiber.yield
とresumeを組み合わせるのが、Fiberの真の使い方です(ただのyield
ではなくFiberクラスのクラスメソッドFiber.yield
であることに注意してください)。
Fiber#resume
を呼び出すと子ファイバーにコンテキストを切り替え、ブロック中でFiber.yield
した時点でまた親にコンテキストを切り替えます。再度resumeすると前回の続きからブロックの処理が始まり、またFiber.yield
で親に戻ってきます。親と子がキャッチボールのように処理を切り替え、「協調的に」動作しています。
また、fiberをresumeできるのは「yieldの数+1回」であり、さらにresumeしようとするとFiberErrorが発生し"dead fiber called"だと怒られることもわかります。
状態を変えながらloopの中で無限にFiber.yield
を呼べるようにすれば、以下のような使い方も可能です。Fiberを用いて、resumeするたびにフィボナッチ数を順々に返すオブジェクトを作っています。
このように「計算を途中までで止めて部分的な結果を返し、また続きから計算を再開する」ようなモノを ジェネレータ と呼びます。FiberはRubyでジェネレータを実装する際の有力な手札となります。
ここまでを踏まえ、再びリファレンスマニュアルから引用します。
Thread クラスが表すスレッドと違い、明示的に指定しない限り ファイバーのコンテキストは切り替わりません。 またファイバーは親子関係を持ちます。Fiber#resume を呼んだファイバーが親になり 呼ばれたファイバーが子になります。親子関係を壊すような遷移(例えば 自分の親の親のファイバーへ切り替えるような処理)はできません。 例外 FiberError が発生します。 できることは
- Fiber#resume により子へコンテキストを切り替える
- Fiber.yield により親へコンテキストを切り替える
の二通りです。
Fiberは親子関係を持ち、親からはresume
、子からはFiber.yield
という非対称な呼び出し関係にあるため、コルーチンではなくセミコルーチンと呼んだほうが正確です。
最後のページでは、非対称な親子関係に制限されない、Fiber#transfer
メソッドの使い方を解説します。