before using this code in production ^Not going to cover Celluloid / Actor model because they are long topics Warnings (part 1) → This is an Intro talk, no Production-ready code here. → This talk is not long enough to cover the Actor Model used in Celluloid and others
definitions. For proper definitions check out: → Working with Ruby Threads by Jessee Storimer → Java Concurrency in Practice by Brian Goetz (Mostly for the definitions) → Seven Concurrency Models in Seven Weeks by Paul Butcher
or units of a program, algorithm, or problem to be executed out-of-order or in partial order, without affecting the final outcome. https://en.wikipedia.org/wiki/ Concurrency(computerscience)
"take turns completing a task", which can make it finish faster, even if we're doing only "one task at a time". We'll talk more about this later. Concurrency
sub-process) that we can use to achieve concurrency. Threads are part of a process and they share the same resources of that process. Threads → Thread: light weight "sub-process" that we can use to achieve concurrency
run. Here we're artificially slowing it down for sake of the example Example without Threads def add(arr) sleep(2) sum = 0 arr.each { |item| sum += item } sum end
considerably. But notice how things got executed out of order. Also each individual call still slept for 2 seconds, so that's a number we cannot go under. Output: arr2 = 15 arr3 = 24 arr1 = 6 (benchmarked using time: real 0m2.038s)
There are others that are particularly popular on other programming languages Concurrency features other languages have built in (and like bragging about) → Promises in ES6 Javascript → Async also in ES6 → Channels in Go
await ^Async means "fire and forget", we request something without waiting for it ^Await means we will "fire on a thread" but wait for this thread to be done. It might execute faster than the regular and it will guarantee when it gets executed. Async horn = Echo.new horn.echo('zero') # synchronous, not thread-safe # returns the actual return value of the method horn.async.echo('one') # asynchronous, non-blocking, thread-safe # returns an IVar in the :pending state horn.await.echo('two') # synchronous, blocking, thread-safe # returns an IVar in the :complete state
of results ^We can even pick a different executor/ thread pool ^Execute asks the Promise to be run Promises → How: p = Concurrent::Promise .fulfill(20) .then { |result| result - 10 } .then { |result| result * 3 } .then(executor: different_executor){ |result| result % 5 }.execute
execution. Also they keep track of rejections which is really helpful to spot where an issue happened Promises → How: p = Concurrent::Promise.execute{ "Hello, world!" } sleep(0.1) p.state #=> :fulfilled p.fulfilled? #=> true p.value #=> "Hello, world!" p = Concurrent::Promise.execute{ raise StandardError.new("Here comes the Boom!") } sleep(0.1) p.state #=> :rejected p.rejected? #=> true p.reason #=> "#<StandardError: Here comes the Boom!>"
be able to queue operations, execute them and have a common place to reference them. → Why: To make code clearer, I want to have a place to send things into.
a channel. ^Select lets us pick the active channel ^Take lets us extract things from a channel Channel → How: c1 = Concurrent::Channel.new c2 = Concurrent::Channel.new Concurrent::Channel.go do sleep(2) c2 << 'two' end Concurrent::Channel.go do sleep(1) c1 << 'one' end 2.times do Concurrent::Channel.select do |s| s.take(c1) { |msg| print "received #{msg}\n" } s.take(c2) { |msg| print "received #{msg}\n" } end end
see a performance boost unless your code is slow or you are processing enough elements → Putting a code in a thread, won't make it run faster (it will just make it run somewhere else, which could make it run sooner) → Compiled code executes faster, so if you need to drastically speed up your code, use JRuby
the time putting deferrable code on a worker (Resque/Sidekiq) should be enough → Again JRuby or even (gasp) using another language might make sense for your problem → You can call code in other languages (C or Rust) from Ruby using FFI → Guaranteeing Thread safety is hard and complex, spend a lot of time testing