Ruby provides three distinct mechanisms for concurrent programming, each suited to different use cases. Understanding when to use Concurrency versus Parallelism helps choose the right approach for your workload.

Threads

The GVL's Hidden Advantage

Ruby’s GVL transforms thread parallelism into a feature for I/O workloads—threads yield the lock during blocking operations, enabling efficient concurrent I/O without the cost of true parallelism.

Ruby threads are native OS threads that share a single Global Interpreter Lock (GVL). While the GVL prevents true parallel execution of Ruby code, threads excel at O Operations where they can yield the lock during blocking calls. See Thread Contention is GVL Queuing in Ruby for details on how thread scheduling works under the GVL.

Fibers

Fibers implement Cooperative Multitasking through Coroutines, allowing developers to explicitly control context switching. They’re lightweight and ideal for structuring asynchronous code, particularly in Async Ruby frameworks that handle thousands of concurrent I/O operations efficiently.

Ractors

Parallelism Through GVL Multiplication

Ractors sidestep Ruby’s parallelism bottleneck not by removing the GVL, but by giving each Ractor its own—turning the GVL from a global constraint into a per-actor boundary.

Ractors achieve true parallelism by giving each Ractor its own GVL, implementing the Actor Pattern for safe concurrent execution. This enables CPU-bound workloads to run across multiple cores, though it requires careful attention to Thread Safety through message passing rather than shared memory.

graph TD
    A[Ruby Concurrency] --> B[Threads]
    A --> C[Fibers]
    A --> D[Ractors]

    B --> E[Single GVL]
    C --> E
    D --> F[Multiple GVLs]

    E --> G[No True Parallelism]
    F --> H[True Parallelism]

    B --> I[I/O-bound Tasks]
    C --> J[Async I/O]
    D --> K[CPU-bound Tasks]

When to Use

The Mechanism Dictates the Workload

Choose threads for I/O concurrency, fibers for structured async patterns, and Ractors only when you need true CPU parallelism—mixing them incorrectly inverts performance characteristics.

Threads: Best for I/O-bound work where operations block on network, file system, or database calls. Simple threading model with automatic scheduling.

Fibers: Ideal for structuring asynchronous I/O patterns with explicit control flow. Enables high-concurrency servers with minimal overhead.

Ractors: Required for true parallel CPU-intensive workloads. Use when you need multiple cores processing Ruby code simultaneously.