Ruby中的Fibers有什么意义?

时间:2013-05-20 20:53:18

标签: ruby-on-rails ruby multithreading fibers

我不明白下面的内容:

counts = Hash.new(0)

File.foreach("testfile") do |line|
  line.scan(/\w+/) do |word|
    word = word.downcase
    counts[word] += 1
  end
end

counts.keys.sort.each {|k| print "#{k}:#{counts[k]} "}

比这更糟糕:

words = Fiber.new do
  File.foreach("testfile") do |line|
    line.scan(/\w+/) do |word|
      Fiber.yield word.downcase
    end
  end
end

counts = Hash.new(0)

while word = words.resume
  counts[word] += 1
end

counts.keys.sort.each {|k| print "#{k}:#{counts[k]} "}

1 个答案:

答案 0 :(得分:26)

光纤是一种暂停和恢复任意代码块的方法。像这样的例子并不是一个很好的用例,因为它没有提供任何真正的优势,而不是传统的阅读和处理方式。

在这个特定的例子中,如果你想让它变得更好,你可以编写一个枚举器式的界面,这样你就可以写:

words = WordsReader.new("testfile")

words.each do |word|
  # ...
end

Fibers变得重要的地方在于编写异步代码。例如,在EventMachine环境中,您需要能够发出异步调用,挂起代码块,并在收到响应时恢复它。

这最终看起来像这样:

async_call(argument1, argument2) do |response_1|
  if (response_1.ok?)
    async_call(argument3, argument4) do |response_2|
      if (response_2.ok?)
        async_call(argument5, argument6) do |response_3|
          if (response_3.ok?)
            do_something(response_1, response_2, response_3)
          else
            panic_and_fail!
          end
        end
      else
        panic_and_fail!
      end
    end
  else
    panic_and_fail!
  end
end

这种嵌套,嵌套和重新嵌套的调用结构被称为“回调地狱”,因为一旦你的逻辑变得非常重要,它就很难管理。平整这种结构的一种方法是使用纤维。适当的纤维化等效物是:

begin
  response_1 = fiber_call(argument1, argument2)
  response_2 = fiber_call(argument3, argument4)
  response_3 = fiber_call(argument5, argument6)

  do_something(response_1, response_2, response_3)

rescue NotOkay
  panic_and_fail!
end

光纤可以利用异常,其中回调类型代码不能。有效使用时,例外可以大规模简化代码块,如您在此处所见。而不是在每个响应上测试ok?,而是预期该调用将抛出类型为NotOkay的异常。

回调无法可靠地抛出异常,因为在回调发生时,调用的发起者已经超出了范围。这是使用回调进行异步编程的基本限制。光纤驱动的代码维护一个正确的调用堆栈,它只是暂停并按原样恢复,因此异常正确地级联通过调用者。

我发现Fibers既易于理解又很难正确应用。大多数情况下,您不必直接使用它们,您将使用一个使用它们的库。编写“光纤感知”代码与编写“线程安全”代码没有什么不同。正确的做法可能很棘手。

相关问题