2013-05-20 27 views
8

我不明白是怎么如下:Ruby中的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]} "} 

回答

24

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

在这个特殊的例子,如果你想变得更好,你会写一个枚举风格的界面,所以你可以写:

words = WordsReader.new("testfile") 

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

凡纤维成为重要的是在编写异步代码。例如,在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类型的异常。

回调不能可靠地抛出异常,因为在回调发生时,调用的发起者已经超出范围。这是使用回调进行异步编程的一个基本限制。光纤驱动的代码维护一个适当的调用栈,它只是暂停和恢复,所以异常通过调用者正确地级联。

我发现纤维既易于理解又非常难以正确应用。大多数情况下你不需要直接使用它们,你将使用一个库来代替它们。编写“光纤感知”代码与编写“线程安全”代码不同。正确的做法可能会非常棘手。

+4

你在17分钟内写完了所有内容? –

+3

显然如此。不知道我正在计时! – tadman

+2

Wow Tadman,我真的很感谢你为了向我和任何可能遇到它的人解释这个深度。对此,我真的非常感激。你好,加拿大同胞! :) – Senjai