Ruby
multithreading
concurrency
programming
software development

Does ruby have real multithreading?

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Ruby is a dynamic, object-oriented programming language well-loved for its simplicity and productivity. However, when developers delve into the mechanics of concurrency and parallelism in Ruby, questions often arise about its true multithreading capabilities. This article explores the intricacies of multithreading in Ruby, providing technical insights and examples.

Understanding Threading in Ruby

To comprehend Ruby’s multithreading, it's essential to differentiate between concurrency and parallelism.

  • Concurrency refers to an application making progress on more than one task at a time, while not necessarily executing them simultaneously.
  • Parallelism involves tasks literally running at the same time, which is only possible on multi-core CPU architectures.

Global Interpreter Lock (GIL)

A key element in Ruby’s threading model is the Global Interpreter Lock (GIL), a mutex that allows only one thread to execute Ruby code at a time. This significantly affects Ruby’s ability to achieve parallelism, especially in CPU-bound tasks.

How GIL Works

Ruby’s GIL exists to:

  • Simplify memory management.
  • Prevent race conditions in native C code in the interpreter.

The GIL ensures that even though multiple native threads can exist, only one can execute Ruby code at any given moment. This means real parallel computation is limited, as threads in a CPU-bound Ruby app can't execute simultaneously on multiple CPU cores.

Concurrency and Parallelism in Ruby

  1. Concurrency: Ruby handles I/O-bound concurrent apps well because it allows threads to switch to other tasks while waiting for I/O operations to complete.
  2. Parallelism Options:
    • For true parallel execution, you need to use multiple processes. This can be done using techniques like:
      • Process.fork: Creates a new child process duplicating the process from which it is called.
      • Parallel gem: Helps in splitting workloads across multiple cores.
      • JRuby: An alternative Ruby interpreter that uses Java threads, allowing true multithreading without a GIL.

Examples

Example: Threads in Ruby

Below is an example of creating threads in Ruby. This showcases concurrency on an I/O-bound operation:

ruby
1require 'net/http'
2
3urls = ["http://example.com", "http://example.org", "http://example.net"]
4
5threads = urls.map do |url|
6  Thread.new(url) do |my_url|
7    response = Net::HTTP.get_response(URI(my_url))
8    puts "Fetched #{my_url}: #{response.body[0..60]}..."
9  end
10end
11
12threads.each(&:join)

Example: Parallelizing Tasks

For true parallel execution, consider using the Parallel gem:

ruby
1require 'parallel'
2
3results = Parallel.map([1, 2, 3, 4, 5]) do |num|
4  num * num
5end
6
7puts results.inspect

This functionality splits tasks across cores and achieves parallelism.

Alternative Implementations

Ruby has several implementations that offer different approaches to multithreading and the GIL:

  • JRuby: Built on the Java Virtual Machine (JVM), JRuby leverages Java’s threading model, enabling true parallel threading without a GIL.
  • Rubinius: Aimed at providing a more native threading model akin to languages like C++.
  • TruffleRuby: Another high-performance implementation that benefits from the GraalVM and offers better support for parallel execution.

Key Points Summary

AspectRuby MRIJRubyRubinius
GIL PresenceYesNoNo
I/O-bound ConcurrencyGoodGoodGood
CPU-bound ParallelismLimited due to GIL (only one thread active)True parallelism achievableImproved parallel capabilities
Alternative UsesUse multiple processes for parallelismJava threading model supporting full parallelismNative thread support

Conclusion

In summary, while Ruby MRI provides concurrency through its thread library, it falls short in terms of parallel execution due to the GIL. However, for true multithreading and parallel computing needs, alternatives like JRuby or employing multi-process architectures are viable solutions. Understanding these nuances allows Ruby developers to leverage the right tool and approach, ensuring efficient resource utilization and optimized performance for the task at hand.


Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.