我在Ruby1.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”之后暂停两秒钟。然而,它只是立即打印出“AC”,然后等待两秒钟后退出。我做错什么了?
(作为参考,我试图在不使用em synchrony的情况下重现this article中描述的em synchrony风格的行为。)
编辑:这里有一些我最终想要完成的细节。我正在开发一个运行在thin上的grape api,在返回响应之前,每个路由处理程序必须对数据存储、zookeeper、其他http服务等进行各种调用。
em-synchrony确实很酷,但我一直遇到根纤维屈服的问题,或者结果显示了上述情况的非同步症状。rack-fiber_pool似乎也很有用,但我不愿意承诺使用它,因为它打破了所有的rack::测试单元测试。
我将问题简化为上面的简单示例,因为我似乎对光纤和eventmachine应该如何一起使用有一个基本的误解,这使我无法有效地使用更复杂的框架。

最佳答案

你可能想要这样的东西:

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"不能在puts "A"之前运行),而“并行”光纤上的动作不能依赖(也不应该关心)其他光纤上的动作是否已执行:我们只有通过交换前两个resume调用才能打印“bacd”。
因此,这里是rack-fiber_pool的神奇之处:它将应用程序接收到的每个请求都放在光纤中(这意味着顺序独立),然后期望您在io操作上Fiber.yield,以便服务器可以接受其他请求。然后,在eventmachine回调中,传入一个包含current_fiber.resume的块,这样当对查询/请求/whatever的响应就绪时,您的纤程将重新启动。
这已经是一个冗长的答案,但如果还不清楚,我可以提供一个eventmachine示例(我知道这是一个毛茸茸的概念,我挣扎了很多)。
更新:我已经创建了一个示例,它可能会帮助那些仍在概念上挣扎的人:https://gist.github.com/renato-zannon/4698724。我建议你跑去玩。

10-07 21:48