Threads and fibers
| Threads | |
| The GVL | |
| Fibers | |
| Mutex |
Threads
A thread runs a block alongside your main program. The main program keeps going immediately — nothing waits unless you ask it to. Call join when you need the result before continuing:
worker = Thread.new do
puts "hello from a thread"
end
worker.join
puts "done"
hello from a thread
done
Without join the program could exit before the thread prints anything. Read the computed value with value instead — it joins implicitly and returns whatever the block evaluated to.
The GVL
CRuby runs threads on top of the Global VM Lock (GVL, historically called
the GIL): only one thread executes Ruby code at a time. Threads still help
whenever a thread waits — network responses, files, sleep — because a
waiting thread releases the lock for the others.
Pure computation does not get faster with more threads on CRuby: two
threads crunching numbers take turns instead of running in parallel. For
CPU-bound work use processes (see lesson 40 tools) or JRuby/TruffleRuby,
which have no GVL.
Fibers
A fiber is cooperative: it runs only when you resume it and pauses only where it yields. No surprises, no races — you control every switch:
fiber = Fiber.new do
Fiber.yield 1
Fiber.yield 2
end
puts fiber.resume
puts fiber.resume
1
2
Mutex
Threads share all variables, so two threads appending at once can corrupt an array. A mutex lets only one thread inside the critical section at a time. Collect the squares of 0, 1, 2 safely and print them sorted:
results = []
mutex = Mutex.new
workers = 3.times.map do |i|
Thread.new do
square = i * i
mutex.synchronize { results << square }
end
end
workers.each(&:join)
puts results.sort.inspect
[0, 1, 4]
Rule of thumb: share as little as possible, guard what you share with a mutex, and join every thread you start.
Article author: Arthur Isaev