一、什么是Fiber?为什么它能实现轻量级并发?

想象你正在厨房同时煮汤和炒菜。传统多线程就像请两个厨师各干各的,而Fiber更像是你自己快速切换这两个任务——汤煮沸时搅动两下,马上翻几下锅里的菜。Ruby的Fiber就是这种"任务切换器",它比线程更轻量,切换成本只有线程的1/10。

技术栈:Ruby 3.2.2

# 创建两个简单的Fiber任务
fiber1 = Fiber.new do
  puts "开始煮汤"
  Fiber.yield # 暂停当前任务
  puts "继续煮汤并完成"
end

fiber2 = Fiber.new do
  puts "开始炒菜"
  Fiber.yield # 暂停当前任务
  puts "继续炒菜并完成"
end

# 交替执行两个任务
fiber1.resume # 输出:开始煮汤
fiber2.resume # 输出:开始炒菜
fiber1.resume # 输出:继续煮汤并完成
fiber2.resume # 输出:继续炒菜并完成

关键点在于Fiber.yieldresume这对搭档。就像视频播放器的暂停/继续按钮,完全由你控制切换时机,不需要操作系统介入。

二、Fiber的四种经典使用模式

模式1:生产者-消费者模型

技术栈:Ruby 3.2.2

producer = Fiber.new do
  (1..3).each do |i|
    puts "生产第#{i}个包子"
    Fiber.yield(i) # 交出控制权并传递数据
  end
  nil # 生产结束
end

consumer = Fiber.new do
  while product = producer.resume
    puts "吃掉#{product}号包子"
  end
  puts "全部吃完了!"
end

consumer.resume
# 输出:
# 生产第1个包子
# 吃掉1号包子
# 生产第2个包子
# 吃掉2号包子
# 生产第3个包子
# 吃掉3号包子
# 全部吃完了!

模式2:管道式处理

技术栈:Ruby 3.2.2

# 数据清洗Fiber
cleaner = Fiber.new do |data|
  puts "清洗数据: #{data}"
  Fiber.yield(data.strip.downcase)
end

# 数据分析Fiber
analyzer = Fiber.new do |data|
  cleaned = cleaner.resume(data)
  puts "分析数据: #{cleaned}"
  Fiber.yield(cleaned.size)
end

# 结果输出Fiber
reporter = Fiber.new do |data|
  length = analyzer.resume(data)
  puts "最终结果: 有效字符#{length}个"
end

reporter.resume("  Ruby FIBER  ")
# 输出:
# 清洗数据:   Ruby FIBER  
# 分析数据: ruby fiber
# 最终结果: 有效字符9个

三、Fiber的高级技巧:自动调度器

Ruby的Fiber可以搭配Fiber.scheduler实现更智能的调度,特别适合I/O密集型任务:

技术栈:Ruby 3.2.2

require 'async'

# 模拟网络请求
def fetch_url(url)
  Async do |task|
    puts "开始请求: #{url}"
    task.sleep(rand(0.1..0.5)) # 模拟网络延迟
    puts "收到响应: #{url}"
    "模拟#{url}的数据"
  end
end

# 创建调度器
Async do
  # 同时发起3个请求
  fibers = %w[url1 url2 url3].map do |url|
    Fiber.new { fetch_url(url) }
  end

  fibers.each(&:resume)
end
# 可能的输出:
# 开始请求: url1
# 开始请求: url2
# 开始请求: url3
# 收到响应: url2
# 收到响应: url1
# 收到响应: url3

四、Fiber的实战应用场景

  1. 网络爬虫:同时维护多个网页抓取会话
  2. 游戏开发:管理多个游戏角色的行为逻辑
  3. 实时数据处理:流式处理日志或传感器数据
  4. GUI应用:保持界面响应同时执行后台任务

技术栈:Ruby 3.2.2

# 模拟游戏场景
player = Fiber.new do
  loop do
    puts "玩家移动"
    Fiber.yield
    puts "玩家攻击"
    Fiber.yield
  end
end

enemy = Fiber.new do
  loop do
    puts "敌人巡逻"
    Fiber.yield
    puts "敌人休息"
    Fiber.yield
  end
end

3.times do
  player.resume
  enemy.resume
end
# 输出:
# 玩家移动
# 敌人巡逻
# 玩家攻击
# 敌人休息
# 玩家移动
# 敌人巡逻

五、技术对比与注意事项

优势

  • 内存占用小(每个Fiber约4KB)
  • 切换速度快(纳秒级)
  • 避免线程安全问题和锁竞争

局限性

  • 不自动利用多核CPU
  • 阻塞操作会冻结整个程序
  • 调试难度较高

重要提醒

  1. 避免在Fiber中执行耗时计算
  2. I/O操作务必使用非阻塞方式
  3. Fiber.current可以获取当前Fiber对象
  4. Ruby 3.0+的性能优化最明显

六、完整案例:文件批量处理系统

技术栈:Ruby 3.2.2

class FileProcessor
  def initialize(files)
    @files = files
    @reader = create_reader
    @parser = create_parser
    @writer = create_writer
  end

  def create_reader
    Fiber.new do
      @files.each do |file|
        puts "读取 #{file}"
        Fiber.yield(File.read(file))
      end
      nil
    end
  end

  def create_parser
    Fiber.new do
      loop do
        content = @reader.resume
        break unless content
        puts "解析 #{content.size}字节数据"
        Fiber.yield(content.upcase)
      end
    end
  end

  def create_writer
    Fiber.new do
      loop do
        parsed = @parser.resume
        break unless parsed
        puts "写入 #{parsed.size}字节数据"
        # 实际项目这里写入数据库/文件
      end
    end
  end

  def process
    @writer.resume until @reader.alive? == false
  end
end

# 使用示例
processor = FileProcessor.new(['file1.txt', 'file2.txt'])
processor.process

七、总结与最佳实践

Fiber就像编程界的"时间管理大师",通过几点建议帮你用好它:

  1. 明确边界:每个Fiber应该专注单一职责
  2. 控制粒度:任务拆分到10-100毫秒级别最合适
  3. 错误处理:用resume返回值判断状态
  4. 性能监控:定期检查Fiber内存使用情况

记住,Fiber不是万能的,但在以下场景特别耀眼:

  • 需要维护大量并发连接时
  • 处理突发性高吞吐量数据流时
  • 传统线程方案遇到性能瓶颈时

最后送大家一个Fiber使用口诀: "轻量任务用纤程,切换自如显神通, 阻塞操作要避开,协作调度效率升。"