为何Ruby Fiber仅在设置调度器后才实现并发执行?
问题解答
核心结论
Ruby的Fiber本身是协作式流控制结构,但Ruby 3.0引入的Fiber调度器API赋予了它实现并发的能力——这就是你观察到两种执行结果差异的根本原因。早期Stack Overflow回答的结论针对的是3.0之前的Ruby版本,和当前特性不矛盾。
1. 未设置调度器时:Fiber串行执行
当你不指定调度器时,Fiber.resume会阻塞当前线程,直到该Fiber完全执行完毕才会继续执行后续代码。你的代码中:
- 每个Fiber里的
sleep 1是Ruby默认的阻塞式sleep,它不会主动触发Fiber让出控制权。 - 第一个Fiber跑完1秒sleep后,第二个Fiber才会被resume,以此类推,5个Fiber总共耗时约5秒。
对应的执行命令与输出:
time bundle exec ruby main.rb
Beginning calculation #1... Beginning calculation #2... Beginning calculation #3... Beginning calculation #4... Beginning calculation #5... Finished all calculations! real 0m5.179s user 0m0.146s sys 0m0.027s
2. 设置调度器后:Fiber并发执行
Ruby 3.0+的核心方法(包括sleep)都适配了Fiber调度器API:
- 当检测到全局调度器(通过
Fiber.set_scheduler设置)存在时,sleep会主动挂起当前Fiber,并通知调度器去调度其他已就绪的Fiber。 libev_scheduler是基于libev的事件驱动调度器,它会在一个Fiber挂起时立刻切换到另一个Fiber执行,因此5个Fiber的sleep 1是并行进行的,总耗时约1秒。
对应的执行命令与输出:
time bundle exec ruby main.rb --set-sched
Beginning calculation #1... Beginning calculation #2... Beginning calculation #3... Beginning calculation #4... Beginning calculation #5... Finished all calculations! real 0m1.173s user 0m0.150s sys 0m0.021s
3. 与早期回答的矛盾解释
早期Stack Overflow回答的结论针对的是Ruby 3.0之前的版本:那时没有Fiber调度器API,Fiber只能通过手动调用Fiber.yield和Fiber.resume实现流控制,无法自动处理阻塞操作来实现并发,所以说它是“流控制结构而非并发工具”是正确的。
而Ruby 3.0引入的调度器API,让Fiber可以结合事件驱动框架(比如libev_scheduler),自动在阻塞操作时让出控制权,从而实现协作式并发——这是Ruby版本迭代带来的新特性,和老回答的结论并不冲突。
你的理解验证
你的思路完全正确:Fiber是协作式并发,需要主动让出控制权才能实现并发。设置调度器后,sleep这类阻塞方法会自动触发Fiber挂起(相当于自动执行Fiber.yield),调度器负责切换到其他Fiber,最终实现了并发执行。
内容的提问来源于stack exchange,提问作者Matt Welke
相关产品推荐
相关产品推荐

