What is GIL in Ruby?
GIL stands for Global Interpreter Lock. It's a mutex (mutual exclusion) used by Ruby’s interpreter — specifically MRI (Matz’s Ruby Interpreter), the default Ruby implementation — to ensure only one thread executes Ruby code at a time.
📌 Why does GIL exist?
Ruby (MRI) is written in C. The GIL is used to:
Simplify memory management in MRI’s C-based interpreter.
Avoid race conditions in the Ruby interpreter internals (especially garbage collection).
🧵 How does it affect threads?
Even if you write multi-threaded Ruby code, only one thread executes at a time (in terms of Ruby code execution). The threads take turns, not run in true parallel — this is known as cooperative concurrency.
Example:
threads = 10.times.map do
Thread.new do
1_000_000.times { |i| i * i }
end
end
threads.each(&:join)
Even though there are 10 threads, due to GIL, only one will run Ruby code at any given time (on a single core). Only one thread will execute Ruby code at a time, regardless of how many CPU cores your machine has.
🧵 But threads are still useful when...
Even though only one thread runs Ruby code at a time, I/O operations release the GIL, so:
✅ Multi-threading works well for:
- HTTP/network requests (e.g., Net::HTTP, open-uri)
- File I/O
- Database queries
- Sleeping/waiting
require 'net/http'
require 'uri'
urls = [
'https://santattech.com',
'https://google.com',
'https://qi.com'
]
threads = urls.map do |url|
Thread.new do
response = Net::HTTP.get(URI(url))
puts "#{url} - #{response.bytesize} bytes"
end
end
threads.each(&:join)
Here, each thread will process an I/O HTTP request. So, GIL will be released.
⚙️ What is parallel then?
Ruby threads can still benefit from parallelism when:
They are waiting on I/O (e.g., file reads, network).
They are running native extensions or C code that release the GIL (e.g., some database drivers, image processing libraries).
So, GIL doesn't block concurrency, but it limits parallelism in CPU-bound Ruby code.
🚀 Alternatives to bypass GIL:
Use processes instead of threads
Use Process.fork
or tools like:
- parallel gem
- sidekiq (for background jobs)
Parallel gem internally uses Process.fork
require 'parallel'
results = Parallel.map([1, 2, 3, 4, 5], in_processes: 3) do |i|
puts "PID #{Process.pid} is processing #{i}"
sleep 1
i * i
end
puts "Results: #{results.inspect}"
NB: I think parallel gem is available inside Rails by default from Rails 7.
Use JRuby
These alternative Ruby implementations don’t have GIL, so they support true parallel threading on multicore CPUs. It uses the JVM (Java Virtual Machine), allowing true multi-threading.
RUBY
JRUBY
Conclusion
GIL is not a problem when the application is doing HTTP requests, I/O operations or the file operations. Also, GIL makes the Ruby code execution simpler & safer, though multi-threading is not allowed. If you need multi-threading can opt for JRuby.
Top comments (0)