我写了一个简单的ruby光纤程序来看看它们是如何工作的,并得到了一个意想不到的结果。
#! /usr/bin/env ruby
#encoding: utf-8
#frozen_string_literal: true
sg = Fiber.new do
File.open(begin print "Enter filename: "; gets.chomp end).each{|l| Fiber.yield l}
end
begin
loop do
puts sg.resume
end
rescue => err
puts "Error: #{err.message}"
end
功能
This is the first
This is the second
This is the third
This is the fourth
This is the fifth
和上面程序的输出
Enter filename: datafile
This is the first
This is the second
This is the third
This is the fourth
This is the fifth
#<File:0x0000557ce26ce3c8>
Error: attempt to resume a terminated fiber
我不知道为什么它在输出中显示#File:0x0000557ce26ce3c8。
说明:ruby 3.0.2p107 (2021-07-07 revision 0db68f0233) [x86_64-linux-gnu]
从文档,
在生成或终止时,
Fiber
返回最后一次执行的表达式的值
除了Fiber.yield
调用之外,光纤(像普通函数一样)返回光纤中最终表达式的结果。
你的纤维体是这样的。
File.open(begin print "Enter filename: "; gets.chomp end).each{|l| Fiber.yield l}
在.each
中,生成文件的每一行,正如您已经观察到的那样打印出来。但是,当纤维完成后,它产生一个最终值,这是File.open
的结果。File.open
返回File
对象本身。所以你的sg.resume
实际上看到了六个结果,而不是五个。
"This is the first"
"This is the second"
"This is the third"
"This is the fourth"
"This is the fifth"
(the file object itself)
这实际上指出了程序开始时的一个小问题:您从不关闭文件。您可以使用File#close
或通过将块传递给File::open
来做到这一点。为了完全安全,你的纤维代码应该看起来像这样。
sg = Fiber.new do
print "Enter filename: "
filename = gets.chomp
# By passing File.open a block, the file is closed after the block automatically.
File.open(filename) do |f|
f.each{|l| Fiber.yield l}
end
# Our fiber has to return something at the end, so let's just return nil
nil
end
begin
loop do
# Get the value. If it's nil, then we're done; we can break out of the loop.
# Otherwise, print it
value = sg.resume
break if value.nil?
puts value
end
rescue => err
puts "Error: #{err.message}"
end
现在,除了处理那个讨厌的文件句柄之外,我们还有一种方法来检测光纤何时结束,并且我们不再收到"尝试恢复已终止的光纤"的消息。错误。