7

Ruby 1.9.2 で次のコード スニペットを実行しています。

require "eventmachine"
require "fiber"

EM.run do
  fiber = Fiber.new do
    current_fiber = Fiber.current
    EM.add_timer(2) do
      print "B"
      current_fiber.resume("D")
    end
    Fiber.yield
  end
  print "A"
  val = fiber.resume
  print "C"
  print val
  EM.stop
end

出力が「ABCD」になり、プログラムが「A」の後に 2 秒間一時停止することを期待しています。ただし、代わりに「AC」をすぐに出力し、終了する前に 2 秒間待機します。私は何を間違っていますか?

(参考までに、em-synchrony を使用せずに、この記事で説明されている em-synchrony スタイルの動作を再現しようとしています。)

編集: ここで、私が最終的に達成しようとしていることについての詳細をいくつか示します。Thin で動作する Grape API を開発しています。各ルート ハンドラは、応答を返す前に、データストア、ZooKeeper、その他の HTTP サービスなどに対してさまざまな呼び出しを連続して行う必要があります。

em-synchrony は非常に優れていますが、ルート ファイバーからの降伏や、上記の場合の非同期の症状を示す結果に関する問題に直面し続けています。rack-fiber_pool も潜在的に役立つように思えますが、すぐに使用できるようにすると、すべての Rack::Test 単体テストが壊れてしまうため、これを使用することにコミットするのは気が進まないのです。

ファイバーと EventMachine を一緒に使用する方法について根本的な誤解があり、より複雑なフレームワークを効果的に使用できないように思われるため、問題を上記の単純な例にまとめました。

4

1 に答える 1

9

おそらく次のようなものが必要でした:

require "eventmachine"
require "fiber"

def value
  current_fiber = Fiber.current

  EM.add_timer(2) do
    puts "B"
    current_fiber.resume("D") # Wakes the fiber
  end

  Fiber.yield # Suspends the Fiber, and returns "D" after #resume is called
end

EM.run do
  Fiber.new {
    puts "A"
    val = value
    puts "C"
    puts val

    EM.stop
  }.resume

  puts "(Async stuff happening)"
end

これにより、次の結果が得られます。

A
(Async stuff happening)
B
C
D

より概念的な説明:

ファイバーは、手動スレッドと同じように、コードのチャンクを一時停止して再アニメートするため、非同期コードのもつれを解くのに役立ちます。これにより、物事が起こる順序に関する巧妙なトリックが可能になります。小さな例:

fiberA = Fiber.new {
  puts "A"
  Fiber.yield
  puts "C"
}

fiberB = Fiber.new {
  puts "B"
  Fiber.yield
  puts "D"
}

fiberA.resume # prints "A"
fiberB.resume # prints "B"
fiberA.resume # prints "C"
fiberB.resume # prints "D"

そのため#resume、ファイバーで が呼び出されると、ブロックの開始 (新しいファイバーの場合) または以前の呼び出しから実行を再開し、別のファイバーが見つかるかブロックが終了Fiber.yieldするまで実行されます。Fiber.yield

一連のアクションをファイバー内に配置することは、それらの間の一時的な依存関係を示す方法であることに注意することが重要です ( puts "C"can't run before puts "A")。一方、「並列」ファイバーでのアクションは当てにできません (そして気にする必要はありません)。 about) 他のファイバーでのアクションが実行されたかどうか: 最初の 2 つのresume呼び出しを交換することによってのみ、"BACD" を出力します。

rack-fiber_poolアプリケーションが受信したすべてのリクエストをファイバー内に配置し (これは順序に依存しないことを意味します)、サーバーが他のリクエストを受け入れることができるように、IO アクションを期待しますFiber.yield。次に、EventMachine コールバック内で、 を含むブロックを渡しますcurrent_fiber.resume。これにより、クエリ/リクエスト/その他の準備が整ったときにファイバーが再アニメーション化されます。

これはすでに長い答えですが、まだ明確でない場合は EventMachine の例を提供できます (これは理解しにくい概念であり、私は多くの苦労をしました)。


更新:まだ概念に苦労しているすべての人に役立つ可能性のある例を作成しました: https://gist.github.com/renato-zannon/4698724。走って遊ぶことをお勧めします。

于 2012-10-04T03:39:13.713 に答える