RubyThreadIntroduction to multithreaded programming with Ruby
Luong VoLuong Vo luong‑komorebiluong‑komorebi
Ruby?
22
Thread?
33
Thread
== Thread of execution
!= Hardware thread
Provides context for CPU
Can be scheduled independently
44
A thread is a basic unit of CPU utilization, which
comprises a thread ID, a program counter, a register set,
and a stack.
55
HardwarevsSoftwarethread
A "hardware thread" is a physical CPU or core.
66
How many thread can a 4 core CPU run at once?
77
4 core CPU can genuinely support 4 hardware threads at
once ‑ the CPU really is doing 4 things at the same time
88
Then we are all limited to 4 threads ??
You are probably using Macbook with a Core i5‑8259U...
99
NO!
1010
1. Intel® Hyper‑Threading Technology
2. Hardware thread can run multiple software threads
3. Hardware thread ‑ OS / Software thread ‑ Language
1111
1212
1313
ThreadScheduling
Imagine reading a book,
then take a break and
comeback....
1414
Ruby
1515
ThreadinRuby
# Initialization 
thr = Thread.new { puts "Whats the big deal" } 
 
# status 
thr.status # => "sleep", "running", 'etc' 
 
# other operations 
thr.kill # should not be used ‑> raise exception 
thr.exit 
thr.stop && thr.wakeup 
thr.pass 
thr.join 
1616
Thread.main == Thread.current 
 
# Your Ruby programm is always running in a main thread. 
# When the main thread exits, all other threads are  
# immediately terminated and the process exits. 
1717
Maximumnumberofthreads
10_000.times do |i| 
  begin 
    Thread.new { sleep } 
  rescue ThreadError 
    puts "Your thread limit is #{i} threads" 
    Kernel.exit(true) 
  end 
end 
 
# it depends, mine is: 4094 
1818
Threadprovidecontexts
Threads share an address space: context, variable and
AST (code tree).
1919
RubymapsitsthreadstoOSnativethreads
100.times do 
  Thread.new { sleep } 
end 
 
puts Process.pid # returns 26600 
sleep 
 
# run in console: `top ‑l1 ‑pid 26600 ‑stats pid,th` 
2020
2121
Benefitsofnativethreads
Run on multiple processors
Scheduled by the OS
Blocking IO operations don't block other threads
2222
Concurrency ? For free ?
2323
Racecondition
2424
Racecondition
Occurs when two or more threads can access shared
data and try to change it at the same time. Because
the thread scheduling algorithm can swap between
threads at any time, you don't know the order in which
the threads will attempt to access the shared data.
2525
Example:FileUpload
2626
files = { 
  'daddario.png' => '*image data*', 
  'elixir.png' => '*image data*' 
} 
 
expected_count = 2 
 
100.times do 
  uploader = FileUploader.new(files) 
  uploader.upload 
  actual_size = uploader.results.size 
  if actual_size != expected_count 
    raise("Race condition, size = #{actual_size}") 
  end 
end 
 
puts 'No race condition this time' 
exit(0) 
2727
  def results 
    # Threads share AST, so here might be a  
    # race condition (one thread creates Queue 
    # then another one creates it again) 
    # To fix: move assignment to #initialize 
    @results ||= Queue.new 
  end 
Threads share an address space: context, variable and
AST (code tree).
2828
Goodpractices
1. Avoid lazy instantiation in multi‑
thread environment
2. Avoid concurrent modifications
3. Protect concurrent modifications
2929
RubyGlobalInterpreterLock
3030
Commonmyth
Ruby is incapable of using threads because of GIL
3131
3232
3333
Facts
1. MRI allows concurrency but prevents parallelism
2. Every Ruby process and process fork has its own GIL
3. MRI releases GIL when thread hits blocking IO (HTTP
request, console IO, etc.). Therefore, blocking IO could
run in parallel
4. Other implementations (JRuby) don't have GIL
3434
ReasonsofGIL
1. Protect Ruby internal C code from race conditions (it is
not always thread safe)
2. Protect calls to C extensions API
3. Protect developers from race conditions
3535
CPUBoundvsIOBound
3636
IO bound
tasks make
sense to use
threads
3737
There always will be a sweet spot between utilization
and context switching and it is important to find it
3838
Computations‑
rich code on
MRI runs better
on 1 thread
while on other
implementations
on N = CPU
cores thread
3939
4040
ThreadSafety
4141
Threadsafecodes:
1. Doesn't corrupt your data
2. Leaves your data consistent
3. Leaves semantics of your program correct
4242
Example: Order a product
Order = Struct.new(:amount, :status) do 
  def pending? 
    status == 'pending' 
  end 
 
  def collect_payment 
    puts "Collecting payment..." 
    self.status = 'paid' 
  end 
end 
 
order = Order.new(100.00, 'pending') 
5.times.map do 
  Thread.new do 
    if order.pending? # check 
      order.collect_payment # set 
    end 
  end 
end.each(&:join) 
4343
Mutex
4444
Mutex
Mutual exclusion, guarantees that no two threads enter
the critical section of code at the same time
Until the owning thread unlocks the mutex, no other
thread can lock it
The guarantee comes from the OS
4545
Mutextips
Use mutex to read value.
Remember: mutexes prevents parallel execution of
critical sections.
Make critical sections as small as possible.
Deadlock may occur when one thread waiting for a
mutex locked by another thread waiting itself for the
first one. Use mutex#try_lock
4646
4747
That'sall.Thankyou
4848
Notyet,actually...
4949
ALWAYSSTARTWITHTHE"WHY"
5050
Whyatthesametime?
Thread is just a terminology. The ultimate reason for its
existence is our demand.
5151
WhyConcurrency?
but not Parallelism
5252
WhyThreads?
but not Processes?
5353
Whyconcurrencywiththreads?
NODEJS ftw!
5454
BeyondRubyThreads
1. Popular application & ecosystem (Bundler, Puma,
Phusion Passenger,...)
2. Concurrency Models (Actors, CSP, STM, Guilds,...)
3. Concurrency Patterns (ThreadPools, Reactor,...)
4. Other languages concurrency implementations
(Golang, Erlang,...)
5555
ThinkBIGGER!
Code:Code: github.com/luong‑komorebi/intro‑to‑ruby‑threads/github.com/luong‑komorebi/intro‑to‑ruby‑threads/
Slide:Slide: https://intro‑to‑ruby‑threads.luongvo.now.sh/https://intro‑to‑ruby‑threads.luongvo.now.sh/ 5656

Introduction to Ruby threads