【问题标题】:Ruby fiber: resuming transferred fibersRuby 光纤:恢复传输的光纤
【发布时间】:2016-07-05 09:01:56
【问题描述】:

我正在尝试了解以下代码 sn-p 的行为。我特别关注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 返回并调用fiber2.resume,我希望在标记为# 10 的行处fiber2 内继续执行。相反,我收到以下错误:

fiber2.rb:24:in `resume': cannot resume transferred Fiber (FiberError)
    from fiber2.rb:24:in `<main>'

这是列表最后一行报告的错误:fiber2.resume

【问题讨论】:

    标签: ruby fibers fiber


    【解决方案1】:

    看来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,并且您不打算在 B 完成之前返回 A .然而,Ruby 没有尾调用优化的概念,所以 A 仍然需要等待 B 完成并产生它的最终值。尽管如此,#transfer 现在基本上是一张单程票。

    【讨论】:

      【解决方案2】:

      您可能在 ruby​​ 中发现了一个错误。当您查看源代码时,它是按照您描述的方式实现的:

      https://fossies.org/linux/misc/ruby-2.3.1.tar.gz/ruby-2.3.1/cont.c

      跟随传输的标志,在传输光纤时设置为1,但永远不会重置。

      IMO 应在光纤增益控制或调用 yield 时重置。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-10-10
        • 2013-12-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-12-29
        • 2015-09-22
        • 1970-01-01
        相关资源
        最近更新 更多