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 }
-
Thread#join
— wait for thread to finish -
Thread#value
—join
then return the last value of thread block -
Thread#status
— return status of thread Thread#wakeup
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:
- Don't do it, if you can avoid it.
- If you must do it, don't share data across threads.
- If you must share data across threads, don't share mutable data.
- 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
HTTP Pool and Work Pool in Gel.
The Practical Effects of the GVL on Scaling in Ruby
How to Test Multithreaded Code