Thread

Work In Progress

Read this book: Working With Ruby Threads.

Read this post: Untangling Ruby Threads.

Use threads allow you to perform tasks concurrently. Using Threads can utilize your multi-core computer. A process can have multiple threads (Puma Web Server). On the otherhand, multiprocess is single thread in a process and have processes per CPU (Unicorn Web Server).

Thread usually costs small overheads for CPU and Memory, see Why does my App's Memory Use Grow Over Time?.

Ruby VM has scheduler decides which threads to run. Switching threads to run things has context switch costs. Ruby also has Fiber, which is lightweight thread.

Source code is at thread.c. The thread implementation was written in Ruby for 2.0. If you want to use Thread within a Rails app, read Threading and Code Execution in Rails.

Threads shares memory. Thread is cheap to use if you can maintain multi-thread safety. Green thread is thread scheduled by Ruby VM that lives in user space. Compares to Native thread scheduled by Operating System that lives in kernel space.

Threads could be interrupted anytime by Scheduler. In other words, Thread Scheduler can interrupt a thread at any time.

Thread.main — The main thread. Created when Ruby Program started.
Thread.current — The current running thread
Thread.list — List all threads
Thread::Queue — thread-safe queue implementation

# you dont need to require thread anymore in newer Ruby
# require "thread"

# We are in the main thread
Thread.main == Thread.current

# Create new threads
Thread.new do
  # do work
end

# Put main thread to sleep
sleep

Or spawn a thread.

Thread.new { puts "A new thread by .new" }
Thread.fork { puts "A new thread by .fork" }
Thread.start("Hello ", "world!") { |s1, s2| puts s1 + s2 }

You can also check if thread is alive or stopped: Thread#alive?, Thread#stop?.

Thread.stop, Thread.pass

CONCURRENCY = 8

def do_work
  puts Random.random_number(10)
end
threads = CONCURRENCY.times.map do
  Thread.new do
    do_work
  end
end

# kill all threads
threads.each(&:join)

Thread.exit
Thread.kill
Thread.pass
Thread.stop
Thread#join
Thread#wakeup
Thread.current.name= — Set thread name

This script can test how many (sleep) threads can you create:

1.upto(100_000) do |i|
  Thread.new { sleep }
  puts i
end

16381
16382
ThreadError: can't create Thread: Resource temporarily unavailable

It is about 16K on macOS. So you can see creating threads are cheap and easy.

From JRuby’s Concurrent Basics, to write concurrent code:

  1. Don't do it, if you can avoid it.
  2. If you must do it, don't share data across threads.
  3. If you must share data across threads, don't share mutable data.
  4. If you must share mutable data across threads, synchronize access to that data.

See also Mutex.

Implement Relation#load_async to schedule the query on the background thread pool

Sidekiq, Puma

HTTP Pool and Work Pool in Gel.

The Practical Effects of the GVL on Scaling in Ruby
How to Test Multithreaded Code

Producer-consumer problem