Skip to content

Instantly share code, notes, and snippets.

@carlzulauf
Last active August 29, 2015 14:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save carlzulauf/d2b4de8ff94eeadc323e to your computer and use it in GitHub Desktop.
Save carlzulauf/d2b4de8ff94eeadc323e to your computer and use it in GitHub Desktop.
Fun With Threads talk
# Class that attempts to have fun with threads
#
# Meant to be used from within pry. Example:
#
# $ pry
# pry(main)> talk = FunWithThreads.new
# pry(main)> cd talk
# pry(#<FWT>)> puts "Hello"
#
class FWT
def perform
# In ruby, there is this thing called Thread
Thread
# Thread seems to be like any other class
Thread.class
# There is already a Thread running. You can inspect it.
Thread.current
# You can make a new thread
#Thread.new # => ArgumentError
# but you have to pass it a block
Thread.new{ do_work_here }
# lets create a new thread by passing it a block
t = Thread.new{ }
# => #<Thread: dead>
# Its dead, our first thread and we've killed it
# killing threads is going to be a recurring theme of this talk
# so an alternative name I considered is...
alt_name
# We're going to learn to keep threads alive a little later
# First, lets talk about work
# Maybe you need to run a long query, or download a file, or resize an image
# ... its going to take a while
# Today we're going to pretend sleeping is working hard
sleep 0.5
# We've got to do something though, so we know work is happening
# Lets just print something to our output
sleep 0.5; p "."
# Lets wrap all this hard work in a proc, so we can reuse it
work = proc{ sleep 0.5; p "." }
# Now we can simulate working hard easily
work.call
work.call
# Lets pretend we have lots of work to do
# The easy solution is just do our work one after the other
10.times(&work)
# This takes a while, and Ruby has to wait for all work to finish before
# it can do anything else
10.times(&work); puts "hello"
# What happens when we start a thread for each unit of work?
10.times{ Thread.new(&work) }
# Even if the work has to happen in order, using a Thread can mean the
# work happens in the background
Thread.new{ 10.times &work }
# We can continue to use Ruby even while the threads are still running
Thread.new{ 10.times &work }; puts "hello"
# while work is going on the thread is alive
t = Thread.new{ 10.times &work }
t.alive?
# after a thread ends its dead
t.alive?
t
# and there is no coming back
# Just like us, once you die, you stay dead
#t.wakeup # => ThreadError
# You can however wakeup a thread when its merely sleeping
# This thread does some work and then sleeps until you wake it up
t = Thread.new{ loop{ p "."; sleep } }
t.wakeup
t.wakeup
# This thread will live for ever
# Unless we murder it, of course
t.kill
# Ruby is flexible, but even in Ruby you can't wake the dead
#t.wakeup # => ThreadError
# To make using threads for background work easier, I made threasy
Threasy.enqueue{ sleep 1; puts "This is in the background" }
Threasy.schedule(in: 1){ puts "This is scheduled" }
# lets start with a new job that identifies itself
job = proc{|i| 10.times{ sleep 0.5; puts "Job #{i}" } }
# Here is what our new jobs look like when run sequentially
2.times(&job); puts "hello"
# Like before, using threads means work occurs in the background
2.times{|i| Thread.new{ job.call i } }; puts "hello"
# If you have to perform this job 100 times, you don't want 100 threads
# Switching between too many threads can cost lots of CPU
# It kinda works, but its not the best solution
100.times{|i| Thread.new{ job.call i } }
# Instead, Threasy will distribute work to a small pool of threads
100.times{|i| Threasy.enqueue{ job.call i } }
Threasy.work.queue.clear
# You can also use threasy to repeat jobs on an interval
entry = Theasy.schedule(every: 0.5){ puts "Repeating job" }
# It's going to continue until you remove the entry from the schedule
entry.remove
end
def initialize
@semaphore = Mutex.new
end
def sync(&block)
@semaphore.synchronize &block
end
def work
proc{ sleep 0.5; p "." }
end
def job
proc{|i| 10.times{ sleep 0.5; puts "Job #{i}" } }
end
def output
STDERR
end
def puts(*a)
sync{ output.puts *a }
end
def p(*a)
sync{ output.print *a }
end
def inspect
%{ (╯°□°)╯︵ ┻━┻ "Fun With Threads" }
end
def alt_name
"😈 🔪 Murdering Threads 🔪 😈 "
end
def clear
10.times{ puts }
end
end
# Use shorter constant for actual classname to reduce pry prompt width
FunWithThreads = FWT
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment