跳到主要内容

Ruby Thread 与 GVL

Ruby 支持 Thread,但在 CRuby 中有 GVL。GVL 是 Global VM Lock 的缩写,它会影响 Ruby 线程并行执行 Ruby 代码的方式。

初学时你不需要把并发模型全部吃透,但要知道:Ruby Thread 能解决一些等待问题,不一定能让 CPU 密集计算变快。

创建线程

thread = Thread.new do
puts "worker"
end

thread.join

join 会等待线程执行结束。

多个线程

threads = 3.times.map do |i|
Thread.new do
puts "thread #{i}"
end
end

threads.each(&:join)

共享数据要小心

counter = 0

threads = 10.times.map do
Thread.new do
1000.times do
counter += 1
end
end
end

threads.each(&:join)
puts counter

共享可变数据容易出现竞态条件。即使有 GVL,也不要把共享修改当作天然安全的设计。

需要同步时可以使用 Mutex

mutex = Mutex.new
counter = 0

threads = 10.times.map do
Thread.new do
1000.times do
mutex.synchronize do
counter += 1
end
end
end
end

threads.each(&:join)
puts counter

GVL 的影响

在 CRuby 中,同一时刻通常只有一个线程执行 Ruby 字节码。I/O 等待型任务仍然可以从线程中获益,因为等待网络或磁盘时,其他线程有机会继续推进。

CPU 密集型任务,例如大量纯 Ruby 数学计算,不一定能因为 Thread 变快。

适合 Thread 的场景

  • 同时等待多个网络请求。
  • 后台处理较轻的任务。
  • 简单并发脚本。
  • 需要保持主流程响应的 I/O 任务。

小结

Ruby Thread 适合 I/O 等待型并发。共享数据时使用同步工具;CPU 密集型任务要考虑进程、Ractor、原生扩展或其他架构。