Ruby fiber:恢复传输的fibers
Ruby fiber: resuming transferred fibers
我试图理解以下代码片段的行为。我的具体重点是 Fiber#transfer
方法。
require 'fiber'
fiber2 = nil
fiber1 = Fiber.new do
puts "In Fiber 1" # 3
fiber2.transfer # 4
end
fiber2 = Fiber.new do
puts "In Fiber 2" # 1
fiber1.transfer # 2
puts "In Fiber 2 again" # 5
Fiber.yield # 6
puts "Fiber 2 resumed" # 10
end
fiber3 = Fiber.new do
puts "In Fiber 3" # 8
end
fiber2.resume # 0
fiber3.resume # 7
fiber2.resume # 9
我已经在右侧用预期的连续执行顺序对代码行进行了编号。一旦 fiber3.resume
returns 并且我调用了 fiber2.resume
,我希望在标记为 #10 的行内 fiber2
内继续执行。相反,我收到以下错误:
fiber2.rb:24:in `resume': cannot resume transferred Fiber (FiberError)
from fiber2.rb:24:in `<main>'
这是清单最后一行报告的错误:fiber2.resume
。
您可能在 ruby 中发现了错误。当你查看源代码时,它是按照你描述的方式实现的:
https://fossies.org/linux/misc/ruby-2.3.1.tar.gz/ruby-2.3.1/cont.c
遵循transferred flag,在传输光纤时设置为1,但永远不会重置。
IMO 它应该在光纤增益控制或调用 yield 时重置。
好像是behavior has changed since Ruby 1.9。在 1.9 中,事情按照提问者假设的方式工作,Ruby 的更高版本改变了 #transfer
的工作方式。我在 2.4 上进行测试,但这可能适用于 2.* 系列中的早期版本。
在 1.9 中,#transfer
可用于在纤维之间来回跳跃。有可能那个时候,#resume
不能用于这个目的。无论如何,在Ruby 2.4中你可以使用#resume
从一根光纤跳转到另一根光纤,然后简单地使用Fiber.yield()
跳回到调用者。
示例(基于问题中的代码):
require 'fiber'
fiber2 = nil
fiber1 = Fiber.new do
puts "In Fiber 1" # 3
Fiber.yield # 4 (returns to fiber2)
end
fiber2 = Fiber.new do
puts "In Fiber 2" # 1
fiber1.resume # 2
puts "In Fiber 2 again" # 5
Fiber.yield # 6 (returns to main)
puts "Fiber 2 resumed" # 10
end
fiber3 = Fiber.new do
puts "In Fiber 3" # 8
end
fiber2.resume # 0
fiber3.resume # 7
fiber2.resume # 9
#transfer
的用例现在似乎是当您有两条光纤(我们称它们为 A 和 B)并且想要从 A 到 B,并且您不打算返回到A 在 B 完成之前。但是,Ruby 没有尾部调用优化的概念,所以 A 仍然必须等待 B 完成并产生它的最终值。尽管如此,#transfer
现在基本上是一张单程票。
我试图理解以下代码片段的行为。我的具体重点是 Fiber#transfer
方法。
require 'fiber'
fiber2 = nil
fiber1 = Fiber.new do
puts "In Fiber 1" # 3
fiber2.transfer # 4
end
fiber2 = Fiber.new do
puts "In Fiber 2" # 1
fiber1.transfer # 2
puts "In Fiber 2 again" # 5
Fiber.yield # 6
puts "Fiber 2 resumed" # 10
end
fiber3 = Fiber.new do
puts "In Fiber 3" # 8
end
fiber2.resume # 0
fiber3.resume # 7
fiber2.resume # 9
我已经在右侧用预期的连续执行顺序对代码行进行了编号。一旦 fiber3.resume
returns 并且我调用了 fiber2.resume
,我希望在标记为 #10 的行内 fiber2
内继续执行。相反,我收到以下错误:
fiber2.rb:24:in `resume': cannot resume transferred Fiber (FiberError)
from fiber2.rb:24:in `<main>'
这是清单最后一行报告的错误:fiber2.resume
。
您可能在 ruby 中发现了错误。当你查看源代码时,它是按照你描述的方式实现的:
https://fossies.org/linux/misc/ruby-2.3.1.tar.gz/ruby-2.3.1/cont.c
遵循transferred flag,在传输光纤时设置为1,但永远不会重置。
IMO 它应该在光纤增益控制或调用 yield 时重置。
好像是behavior has changed since Ruby 1.9。在 1.9 中,事情按照提问者假设的方式工作,Ruby 的更高版本改变了 #transfer
的工作方式。我在 2.4 上进行测试,但这可能适用于 2.* 系列中的早期版本。
在 1.9 中,#transfer
可用于在纤维之间来回跳跃。有可能那个时候,#resume
不能用于这个目的。无论如何,在Ruby 2.4中你可以使用#resume
从一根光纤跳转到另一根光纤,然后简单地使用Fiber.yield()
跳回到调用者。
示例(基于问题中的代码):
require 'fiber'
fiber2 = nil
fiber1 = Fiber.new do
puts "In Fiber 1" # 3
Fiber.yield # 4 (returns to fiber2)
end
fiber2 = Fiber.new do
puts "In Fiber 2" # 1
fiber1.resume # 2
puts "In Fiber 2 again" # 5
Fiber.yield # 6 (returns to main)
puts "Fiber 2 resumed" # 10
end
fiber3 = Fiber.new do
puts "In Fiber 3" # 8
end
fiber2.resume # 0
fiber3.resume # 7
fiber2.resume # 9
#transfer
的用例现在似乎是当您有两条光纤(我们称它们为 A 和 B)并且想要从 A 到 B,并且您不打算返回到A 在 B 完成之前。但是,Ruby 没有尾部调用优化的概念,所以 A 仍然必须等待 B 完成并产生它的最终值。尽管如此,#transfer
现在基本上是一张单程票。