Last active
August 29, 2015 14:03
-
-
Save carlzulauf/d2b4de8ff94eeadc323e to your computer and use it in GitHub Desktop.
Fun With Threads talk
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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