Atomic Counters in Ruby

Our primary mechanism for managing state in Ruby is through communication over threads. However, there are a few other options for managing state. Here we’ll look at using the concurrent-ruby gem for atomic counters accessed by multiple threads.

require 'concurrent'

# We'll use a Concurrent::AtomicFixnum to represent our
# (always-positive) counter.
ops = Concurrent::AtomicFixnum.new(0)

# We'll use an array to keep track of our threads
threads = []

# We'll start 50 threads that each increment the
# counter exactly 1000 times.
50.times do
  threads << Thread.new do
    1000.times do
      # To atomically increment the counter we use #increment
      ops.increment
    end
  end
end

# Wait until all the threads are done.
threads.each(&:join)

# It's safe to read the value even while other threads
# might still be updating it.
puts "ops: #{ops.value}"

To run this program, you’ll need to install the concurrent-ruby gem first:

$ gem install concurrent-ruby
$ ruby atomic_counters.rb
ops: 50000

We expect to get exactly 50,000 operations. Had we used a non-atomic integer and incremented it with ops += 1, we’d likely get a different number, changing between runs, because the threads would interfere with each other.

In Ruby, we don’t have a built-in atomic integer type like in some other languages. Instead, we use the concurrent-ruby gem which provides thread-safe data structures and utilities for concurrent programming in Ruby.

The Concurrent::AtomicFixnum class is used here to provide atomic operations on an integer value. The #increment method is atomic, ensuring that increments from different threads don’t interfere with each other.

Instead of goroutines and WaitGroups, we use Ruby’s native Thread class to create and manage our concurrent operations. We collect all threads in an array and then use #join to wait for all of them to complete before printing the final result.

This example demonstrates how to perform thread-safe operations in Ruby, which is crucial when dealing with shared state in concurrent programs.

Next, we’ll look at mutexes, another tool for managing state in concurrent programs.