You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何非阻塞恢复Ruby Fiber?如何实现多Fiber并发运行?

Ruby Fiber 相关技术问题解答

问题说明

我有两个关于Ruby Fiber的技术问题:

  1. 如何以非阻塞方式恢复Fiber?
  2. 如何让两个Fiber实现并发运行?

当前的测试代码是串行执行两个包含sleep操作的Fiber,总耗时约2秒:

f1 = Fiber.new { sleep 1 }
f2 = Fiber.new { sleep 1 }

start = Time.now
f1.resume
f2.resume
p (Time.now - start).round # => 2
# 期望改写成类似这样的逻辑:
# f1.non_blocking_start
# f2.non_blocking_start
# f1.blocking_await
# f2.blocking_await

在JavaScript(TypeScript)中,可以通过以下代码实现并发效果(总耗时约1秒),由于Ruby Fiber和JS Async原理相近,希望了解Ruby中的实现方式:

function sleep(ms: number): Promise<void> { 
  return new Promise((resolve) => setTimeout(() => resolve())) 
}

let f1 = async function(): Promise<void> { await sleep(1000) }
let f2 = async function(): Promise<void> { await sleep(1000) }

async function main() {
  let p1 = f1()
  let p2 = f2()

  await p1
  await p2
}
main()

问题解答

1. 非阻塞恢复Fiber的实现方式

Ruby原生的Fiber.resume是阻塞式的——当Fiber执行到诸如sleep这类阻塞操作时,会卡住整个线程。要实现非阻塞恢复,核心是结合非阻塞IO与Fiber调度器:让Fiber在遇到阻塞操作时主动让出执行权,由调度器在IO操作完成后再恢复该Fiber的执行。

从Ruby 3.0开始,标准库提供了Fiber.scheduler接口,可用于实现用户态的Fiber调度;也可以使用第三方的async gem简化开发。

2. 让两个Fiber并发运行的具体实现

Fiber是协作式多任务模型,需要调度器来管理Fiber的切换——当一个Fiber进入阻塞状态时,调度器自动切换到其他可运行的Fiber,从而实现“并发”效果。

方式一:使用Ruby 3.0+ 内置调度器

# 封装非阻塞sleep,基于内置调度器实现
def sleep_non_blocking(seconds)
  Fiber.scheduler.wait(Process.clock_gettime(Process::CLOCK_MONOTONIC) + seconds)
end

# 创建包含非阻塞操作的Fiber
f1 = Fiber.new do
  puts "Fiber 1 启动"
  sleep_non_blocking(1)
  puts "Fiber 1 完成"
end

f2 = Fiber.new do
  puts "Fiber 2 启动"
  sleep_non_blocking(1)
  puts "Fiber 2 完成"
end

start = Time.now

# 启用调度器,批量运行Fiber
Fiber.set_scheduler(Fiber::Scheduler.new)
Fiber.scheduler do
  f1.resume
  f2.resume
end

puts "总耗时: #{(Time.now - start).round}秒" # 输出:1

方式二:使用async gem(更简洁)

先安装gem:

gem install async

实现代码:

require 'async'

def sleep_non_blocking(seconds)
  Async::Task.current.sleep(seconds)
end

Async do |root_task|
  start = Time.now

  # 并发启动两个任务(底层基于Fiber)
  task1 = root_task.async do
    puts "Fiber 1 启动"
    sleep_non_blocking(1)
    puts "Fiber 1 完成"
  end

  task2 = root_task.async do
    puts "Fiber 2 启动"
    sleep_non_blocking(1)
    puts "Fiber 2 完成"
  end

  # 等待两个任务结束
  task1.wait
  task2.wait

  puts "总耗时: #{(Time.now - start).round}秒" # 输出:1
end

原理对比

JS的Async/Await依赖浏览器/Node.js的事件循环自动调度Promise;而Ruby Fiber需要显式的调度器来管理切换——当Fiber执行非阻塞操作时,调度器会将其挂起,转而执行其他Fiber,直到阻塞操作完成后再恢复,以此实现类似JS的并发效果。


内容的提问来源于stack exchange,提问作者Alex Craft

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 08:42:45