Understanding Ruby’s Concurrency Models: Processes, Threads, and Actors

In the world of programming, concurrency plays a vital role in improving the efficiency and performance of applications. Concurrency allows programs to execute multiple tasks simultaneously, taking full advantage of modern multi-core processors. Ruby, a dynamic and versatile programming language, offers several concurrency models to achieve parallelism: processes, threads, and actors. In this blog post, we will delve into each of these concurrency models, understand their nuances, and explore code samples to illustrate their usage.

Understanding Ruby's Concurrency Models: Processes, Threads, and Actors

1. Introduction to Concurrency Models

Concurrency is the ability of a system to execute multiple tasks concurrently, making the most of available resources. Ruby, being a language focused on developer productivity, offers multiple concurrency models to cater to different scenarios. These models are processes, threads, and actors. Understanding their differences is essential to select the appropriate model for your application’s requirements.

2. Processes: Isolated Execution Environments

Processes are independent execution units with their memory space, resources, and state. They offer a high level of isolation, making them suitable for situations where fault tolerance and separation are critical.

2.1. Creating Processes

Ruby’s Process module provides methods to create new processes. Each process runs independently, allowing parallel execution of tasks. Here’s a simple code snippet:

# Creating a new process
pid = Process.fork do
  puts "Child process: Hello from child!"

# The parent process continues here
if pid
  puts "Parent process: Child process is done."

2.2. Inter-Process Communication

Processes can communicate through various mechanisms like pipes, sockets, and shared memory. Here’s an example of using pipes for communication:

reader, writer = IO.pipe

Process.fork do
  reader.puts "Message from child process."

puts "Parent process received: #{reader.gets}"

3. Threads: Lightweight Parallelism

Threads are lighter-weight than processes, as they share the same memory space. They are suitable for tasks that involve I/O-bound operations or parallelism without the need for strict isolation.

3.1. Creating Threads

Ruby’s Thread class enables thread creation. Here’s a basic example:

# Creating threads
thread1 = { puts "Thread 1: Hello from thread!" }
thread2 = { puts "Thread 2: Hello from thread!" }


3.2. Thread Safety and Synchronization

Since threads share memory, concurrent access to shared resources can lead to data corruption or race conditions. Ruby offers synchronization mechanisms like Mutex, ensuring only one thread accesses critical sections at a time:

counter = 0
mutex =

# Updating shared counter safely using a mutex
threads = do do
    mutex.synchronize do
      counter += 1

puts "Counter value: #{counter}"

4. Actors: Managing Concurrency through Isolation

Actors are a higher-level concurrency model introduced by the “Celluloid” gem. They provide isolation by encapsulating both state and behavior, communicating only through message passing. This model is suitable for systems requiring high concurrency and easy management of parallel tasks.

4.1. Implementing Actors

First, install the “Celluloid” gem:

gem install celluloid

Now, let’s create a simple actor:

require 'celluloid'

class MyActor
  include Celluloid

  def initialize
    @counter = 0

  def increment
    @counter += 1

  def get_counter

# Create an instance of the actor
actor =

# Communicate with the actor using messages
puts "Counter value from actor: #{actor.get_counter}"

4.2. Message Passing and State Isolation

Actors communicate exclusively through messages, which ensures that their state remains isolated. This approach prevents data corruption and simplifies concurrent programming. Here’s an example:

class MessageActor
  include Celluloid

  def initialize
    @messages = []

  def add_message(message)

  def get_messages

message_actor =
message_actor.async.add_message("Hello from message 1")
message_actor.async.add_message("Hello from message 2")
puts "Messages from actor: #{message_actor.get_messages}"

5. Choosing the Right Concurrency Model

Selecting the appropriate concurrency model depends on your application’s requirements. Use processes for high isolation and fault tolerance, threads for lightweight parallelism, and actors for managing concurrency through encapsulation.


Ruby’s concurrency models – processes, threads, and actors – offer different ways to achieve parallelism and handle concurrent tasks effectively. Understanding the strengths and weaknesses of each model is crucial for designing robust and high-performance applications. By choosing the right concurrency model based on your application’s needs, you can harness the power of parallel programming in the Ruby programming language.

Previously at
Flag Argentina
time icon
Experienced software professional with a strong focus on Ruby. Over 10 years in software development, including B2B SaaS platforms and geolocation-based apps.