Ruby

Does ruby have real multithreading

25 September 2026 · 9 min read

Does ruby have real multithreading

Does Ruby have real multithreading? This question often sparks debate among developers. While Ruby indeed offers threading capabilities, its implementation has nuances that distinguish it from the “true” multithreading found in languages like Java or C++. Understanding these nuances is crucial for leveraging concurrency effectively in Ruby applications and avoiding potential performance bottlenecks. This article delves into the specifics of Ruby’s threading model, exploring its strengths, limitations, and how it impacts real-world application development.

Understanding Ruby’s Threading Model

Ruby employs a threading model often referred to as “green threads” or “M:N threading.” This means multiple Ruby threads are mapped onto a smaller number of operating system (OS) threads. This mapping is managed by the Ruby interpreter, not the operating system kernel. A key implication of this model is the Global Interpreter Lock (GIL).

The GIL, in essence, allows only one Ruby thread to execute Ruby code at any given time, even on multi-core processors. This serialization of Ruby code execution impacts performance, especially for CPU-bound tasks. While multiple threads can exist concurrently, they are effectively time-sliced, taking turns executing within the confines of the GIL.

However, it’s crucial to understand that the GIL doesn’t completely negate the benefits of threading in Ruby. Operations that are not bound by the GIL, such as I/O operations (network requests, file system access), can still occur concurrently. This makes Ruby threading beneficial for I/O-bound applications.

The Impact of the GIL on Performance

The GIL’s impact is most pronounced when dealing with CPU-intensive operations. If your application performs complex calculations or heavy data processing, utilizing multiple Ruby threads might not yield the expected performance gains on multi-core systems. This is because the GIL prevents true parallelism in Ruby code execution.

For instance, imagine a Ruby program processing a large dataset. Even with multiple threads, each thread will have to wait its turn to acquire the GIL before executing the computationally intensive parts of the code. This bottleneck can limit the effective utilization of multiple cores.

However, for I/O-bound tasks, the picture is different. When a thread is waiting for an I/O operation to complete (e.g., a network request), the GIL is released, allowing other threads to execute. This concurrency can significantly improve the performance of I/O-bound applications.

When to Use Ruby Threads

Despite the limitations imposed by the GIL, Ruby threads are valuable tools in certain scenarios. They excel in I/O-bound operations, where waiting for external resources constitutes a significant portion of the processing time. Web servers, network applications, and programs interacting with external APIs can benefit greatly from Ruby’s threading capabilities.

Furthermore, Ruby threads offer a relatively simple way to introduce concurrency into your application. The syntax is straightforward, and the overhead of creating and managing threads is relatively low. This ease of use makes them a practical choice for improving responsiveness and handling multiple concurrent I/O operations.

Consider using threads when:

  • Your application is primarily I/O-bound.
  • You need to improve responsiveness and handle concurrent I/O.
  • Simplicity and ease of implementation are priorities.

Alternatives to Threads for CPU-Bound Tasks

For CPU-bound tasks, where the GIL becomes a significant bottleneck, alternative approaches offer better performance on multi-core systems. These include:

  1. Multiprocessing: Ruby’s Process class allows you to create multiple operating system processes, each with its own interpreter and memory space. This bypasses the GIL entirely, enabling true parallelism for CPU-bound tasks. However, multiprocessing involves higher overhead compared to threading.
  2. JRuby or TruffleRuby: These alternative Ruby implementations have different threading models that do not impose a GIL, allowing for true parallel execution of Ruby code.
  3. Using C Extensions: For performance-critical sections of code, you can implement them as C extensions. This allows you to bypass the GIL and utilize native threads for parallel execution.

Choosing the right approach depends on the specific requirements of your application. Balancing performance gains with development complexity is key.

Infographic Placeholder: Visual representation of Ruby’s threading model vs. multiprocessing.

Frequently Asked Questions

Q: Does Ruby’s GIL make threading useless?

A: No, threading remains valuable for I/O-bound applications, where waiting for external resources dominates the processing time.

Q: How can I achieve true parallelism in Ruby?

A: Consider multiprocessing, JRuby/TruffleRuby, or C extensions for CPU-bound tasks.

Navigating the complexities of concurrency requires careful consideration of the tools at hand. While Ruby’s threading model has its limitations, understanding the role of the GIL and the distinction between I/O-bound and CPU-bound tasks empowers you to make informed decisions. By leveraging threads strategically and exploring alternative concurrency mechanisms where appropriate, you can build efficient and scalable Ruby applications that maximize resource utilization. Learn more about performance optimization techniques on our blog. Explore further resources on Ruby concurrency: Ruby’s Thread Documentation, A deeper dive into the GIL, and Concurrency in Ruby. Consider these insights as you embark on your next Ruby project. Optimizing for concurrency can significantly elevate the performance and responsiveness of your application.

Question & Answer :
I know about the “cooperative” threading of ruby using green threads. How can I create real “OS-level” threads in my application in order to make use of multiple cpu cores for processing?

Updated with Jörg’s Sept 2011 comment

You seem to be confusing two very different things here: the Ruby Programming Language and the specific threading model of one specific implementation of the Ruby Programming Language. There are currently around 11 different implementations of the Ruby Programming Language, with very different and unique threading models.

(Unfortunately, only two of those 11 implementations are actually ready for production use, but by the end of the year that number will probably go up to four or five.) (Update: it’s now 5: MRI, JRuby, YARV (the interpreter for Ruby 1.9), Rubinius and IronRuby).

  1. The first implementation doesn’t actually have a name, which makes it quite awkward to refer to it and is really annoying and confusing. It is most often referred to as “Ruby”, which is even more annoying and confusing than having no name, because it leads to endless confusion between the features of the Ruby Programming Language and a particular Ruby Implementation.

It is also sometimes called “MRI” (for “Matz’s Ruby Implementation”), CRuby or MatzRuby.

MRI implements Ruby Threads as Green Threads within its interpreter. Unfortunately, it doesn’t allow those threads to be scheduled in parallel, they can only run one thread at a time.

However, any number of C Threads (POSIX Threads etc.) can run in parallel to the Ruby Thread, so external C Libraries, or MRI C Extensions that create threads of their own can still run in parallel.

  1. The second implementation is YARV (short for “Yet Another Ruby VM”). YARV implements Ruby Threads as POSIX or Windows NT Threads, however, it uses a Global Interpreter Lock (GIL) to ensure that only one Ruby Thread can actually be scheduled at any one time.

Like MRI, C Threads can actually run parallel to Ruby Threads.

In the future, it is possible, that the GIL might get broken down into more fine-grained locks, thus allowing more and more code to actually run in parallel, but that’s so far away, it is not even planned yet.

  1. JRuby implements Ruby Threads as Native Threads, where “Native Threads” in case of the JVM obviously means “JVM Threads”. JRuby imposes no additional locking on them. So, whether those threads can actually run in parallel depends on the JVM: some JVMs implement JVM Threads as OS Threads and some as Green Threads. (The mainstream JVMs from Sun/Oracle use exclusively OS threads since JDK 1.3)
  2. XRuby also implements Ruby Threads as JVM Threads. Update: XRuby is dead.
  3. IronRuby implements Ruby Threads as Native Threads, where “Native Threads” in case of the CLR obviously means “CLR Threads”. IronRuby imposes no additional locking on them, so, they should run in parallel, as long as your CLR supports that.
  4. Ruby.NET also implements Ruby Threads as CLR Threads. Update: Ruby.NET is dead.
  5. Rubinius implements Ruby Threads as Green Threads within its Virtual Machine. More precisely: the Rubinius VM exports a very lightweight, very flexible concurrency/parallelism/non-local control-flow construct, called a “Task”, and all other concurrency constructs (Threads in this discussion, but also Continuations, Actors and other stuff) are implemented in pure Ruby, using Tasks.

Rubinius can not (currently) schedule Threads in parallel, however, adding that isn’t too much of a problem: Rubinius can already run several VM instances in several POSIX Threads in parallel, within one Rubinius process. Since Threads are actually implemented in Ruby, they can, like any other Ruby object, be serialized and sent to a different VM in a different POSIX Thread. (That’s the same model the BEAM Erlang VM uses for SMP concurrency. It is already implemented for Rubinius Actors.)

Update: The information about Rubinius in this answer is about the Shotgun VM, which doesn’t exist anymore. The “new” C++ VM does not use green threads scheduled across multiple VMs (i.e. Erlang/BEAM style), it uses a more traditional single VM with multiple native OS threads model, just like the one employed by, say, the CLR, Mono, and pretty much every JVM.

  1. MacRuby started out as a port of YARV on top of the Objective-C Runtime and CoreFoundation and Cocoa Frameworks. It has now significantly diverged from YARV, but AFAIK it currently still shares the same Threading Model with YARV. Update: MacRuby depends on apples garbage collector which is declared deprecated and will be removed in later versions of MacOSX, MacRuby is undead.
  2. Cardinal is a Ruby Implementation for the Parrot Virtual Machine. It doesn’t implement threads yet, however, when it does, it will probably implement them as Parrot Threads. Update: Cardinal seems very inactive/dead.
  3. MagLev is a Ruby Implementation for the GemStone/S Smalltalk VM. I have no information what threading model GemStone/S uses, what threading model MagLev uses or even if threads are even implemented yet (probably not).
  4. HotRuby is not a full Ruby Implementation of its own. It is an implementation of a YARV bytecode VM in JavaScript. HotRuby doesn’t support threads (yet?) and when it does, they won’t be able to run in parallel, because JavaScript has no support for true parallelism. There is an ActionScript version of HotRuby, however, and ActionScript might actually support parallelism. Update: HotRuby is dead.

Unfortunately, only two of these 11 Ruby Implementations are actually production-ready: MRI and JRuby.

So, if you want true parallel threads, JRuby is currently your only choice – not that that’s a bad one: JRuby is actually faster than MRI, and arguably more stable.

Otherwise, the “classical” Ruby solution is to use processes instead of threads for parallelism. The Ruby Core Library contains the Process module with the Process.fork method which makes it dead easy to fork off another Ruby process. Also, the Ruby Standard Library contains the Distributed Ruby (dRuby / dRb) library, which allows Ruby code to be trivially distributed across multiple processes, not only on the same machine but also across the network.