Organized Thread Collisions
·We don’t like when things collide.
Photo by szlea
But what if we need them to? What if we want to ensure that should a collision between threads happen, things will not go out of hand?
This is remarkably easy to do. Let’s pretend we want $somevar
to be thread-safe:
require 'test/unit'
class Collision < Test::Unit::TestCase
def test_that_fails
a = Thread.new do
5.times do | t |
$somevar = true
sleep(rand(10)/200.0)
assert_equal true, $somevar,
"Somevar should keep the state true"
end
end
b = Thread.new do
5.times do | t |
$somevar = false
sleep(rand(10)/200.0)
assert_equal false, $somevar,
"Somevar should keep the state false"
end
end
ensure
a.join; b.join
end
end
Fail. Global variables are never thread-safe, neither are class variables and module attributes.
The .times call is somewhat cargo cult programming, because this fails for me on first iteration. Replace the $somevar with your getter and setter and you should see negative results immediately.
How does it work? Simple - we are spinning off two threads, they set the variable and wait in Kernel#sleep
for the other thread to arrive and do it’s own assignment. It ensures that between the variable assignment and the assertion there’s just enough time for another thread to set the new value for the variable.