【问题标题】:Demystifying Ruby "Fiber" internals揭开 Ruby“纤维”内部的神秘面纱
【发布时间】:2020-11-08 17:30:30
【问题描述】:

我正在学习 Ruby Fiber,它提供了很大的灵活性,但我对 Fiber 和 Ruby 提出了一些疑问。

require 'fiber'

class MyObj
    def call_yield
      print "Prepare to Yield"
      print "foo bar"
      Fiber.yield
      print "Resumed"
      @fiber = nil
    end 

    def create_fiber
      #@fiber = Fiber.new{call_yield}
      @fiber = Fiber.new {}
      p @fiber
    end

    def update
      p @fiber.resume
    end
end
  
obj = MyObj.new
# obj.call_yield #Fiber error 
obj.create_fiber
obj.update
obj.update
  1. 当我取消注释带有obj.call_yield 的行时,Fiber 错误会按预期发生。即使我在 irb 会话中键入 Fiber.yield,也会发生相同的错误。

    • ruby 如何识别当前光纤是否有效?
    • 包含Fiber.Yield 的块的范围是什么给Fiber.new
  2. 请注意 create fiber 方法中的 #@fiber = Fiber.new{call_yield} 行仍然是有效代码。

    • 我不知道那里会发生什么,因为函数被直接调用而不是符号。
    • 即使是直接调用函数我在屏幕上也看不到任何内容。这表明函数内部的代码仍然没有开始运行,但是为什么会出现这种行为?
  3. 由于 Fiber 是用 C 语言编写的,因此我没有其他来源可以消除我的疑问,因此请为每个问题提供尽可能简单的示例。

【问题讨论】:

  • "作为函数被直接调用,但不是作为符号" - 不,不是。对call_yield 的调用在一个块中。当 Fiber 决定调用该块时,就会调用该块。
  • "即使函数调用直接发生,我也看不到屏幕上的任何内容。" - 因为你忘记注释掉下一行,用Fiber.new{} 覆盖@fiber
  • 谢谢,但我的主要疑问是关于光纤错误的问题 1。如何在纯 ruby​​ 中重现此类错误?
  • 不确定您所说的“纯红宝石”是什么意思。这段代码已经很简单了。
  • 我想知道Fiber.yield 在提供给Fiber.new 的块内与程序中的任何其他位置相比如何表现,即)它如何在程序的其他地方产生“根光纤错误”?

标签: ruby fibers


【解决方案1】:

我想知道Fiber.yield 在提供给Fiber.new 的块内与程序中的任何其他位置相比如何表现,即)它如何在程序的其他地方产生“根光纤错误”?

啊,其实很简单。您需要做的就是在执行resume 时修改一个全局状态。例如,将当前光纤推送到全局堆栈或类似的东西上。这是一个表现出相同行为的假 Fiber 类。

class MyFiber
  class << self
    def stack
      @stack ||= []
    end
    
    def yield
      top = stack.pop
      if top.nil?
        raise 'root fiber error'
      end
      puts "running fiber #{top}"
    end
  end
  
  def initialize(&block)
    @block = block
  end
  
  def resume
    MyFiber.stack.push(@block)
    @block.call
  end
end

【讨论】:

  • 可接受的解决方案,但不能像原始 Fiber 一样在假 Fiber 类中多次调用 yield。但我知道原始 Fiber 实现了更多方法来处理这些问题。
猜你喜欢
  • 2018-04-17
  • 2011-04-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-08
  • 2023-03-28
  • 1970-01-01
  • 2020-04-29
相关资源
最近更新 更多