使用Ruby Fiber读取文件时出现意外输出结果的问题咨询
问题成因
输出File实例信息是Ruby方法返回值规则与Fiber运行机制共同导致的,核心逻辑:
- Ruby中所有方法执行完毕都有返回值,
File#each完成全文件行遍历后,返回值是调用该方法的File实例自身。 - Fiber执行规则:当Fiber块内所有代码运行完成、没有触发新的
Fiber.yield时,Fiber会把块内最后一个表达式的结果作为本次resume的返回值,随即进入终止状态,后续再调用resume就会抛出attempt to resume a terminated fiber错误。
逐轮对应代码执行流程和输出的匹配关系:
- 首次调用
sg.resume进入Fiber,触发文件名输入提示,输入datafile后打开目标文件,启动each遍历,读到第一行时触发Fiber.yield,将第一行文本返回外层打印。 - 后续连续4次调用
sg.resume时,each块依次读取剩余4行内容,逐行通过Fiber.yield返回,因此依次打印文件内剩下的4行内容。 - 5行内容全部遍历完成后,
File#each执行结束,返回对应的File实例。此时Fiber块内无剩余代码,因此第6次调用sg.resume拿到的返回值就是这个File实例,被外层puts打印,即出现#<File:0x0000557ce26ce3c8>这行输出。 - 本次
resume执行完后Fiber已处于终止状态,下一轮循环再次调用sg.resume时触发错误,被异常捕获逻辑打印。
写法优化
原始代码存在隐含的资源泄漏问题:未给File.open传入代码块,打开的文件句柄不会主动关闭,需要等待GC回收才会释放。建议使用块形式打开文件自动管理句柄,如果不需要额外的返回值,可以在遍历结束后主动返回nil,避免File实例被意外打印:
sg = Fiber.new do print "Enter filename: " filename = gets.chomp File.open(filename) do |f| f.each { |line| Fiber.yield line } end nil end
如果希望遍历结束后循环正常退出、不触发终止fiber的错误,可以在Fiber块末尾主动抛出StopIteration异常,loop会自动捕获该异常正常终止,不需要额外的全局rescue。
内容的提问来源于stack exchange,提问作者user3158810
相关产品推荐
相关产品推荐

