我在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“之后暂停两秒钟。但是,它只是立即打印出"AC“,然后等待两秒钟,然后退出。我做错了什么?
(作为参考,我尝试在不使用em-synchrony的情况下重现this article中描述的em-synchrony样式的行为。)
编辑:下面是关于我最终想要完成的事情的更多细节。我正在开发一个在Thin上运行的Grape,在返回响应之前,每个路由处理程序都必须对数据存储、ZooKeeper、其他HTTP服务等进行各种顺序的调用。
em-synchrony真的很酷,但我总是遇到从根光纤产生的问题,或者结果显示上述情况的非同步症状。rack-fiber_pool似乎也有潜在的用处,但我不愿意使用它,因为它会破坏我所有的Rack::Test单元测试。
我将我的问题简化为上面的简单示例,因为我似乎对fibers和EventMachine应该如何一起使用有一个根本性的误解,这阻碍了我有效地使用更复杂的框架。
发布于 2012-10-04 11:39:14
你可能想要这样的东西:
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的块,以便在准备好对查询/请求/任何内容的响应时重新激活纤程。
这已经是一个冗长的答案,但如果仍然不清楚,我可以提供一个EventMachine示例(我知道这是一个难以理解的概念,我努力了很多)。
更新:我已经创建了一个示例,它可能会帮助任何还在为概念而苦苦挣扎的人:https://gist.github.com/renato-zannon/4698724。我建议你跑着玩它。
https://stackoverflow.com/questions/12719781
复制相似问题