De la Wikipedia sobre Race_condition
A race condition or race hazard is the behavior of an electronic or software system where the output is dependent on the sequence or timing of other uncontrollable events.
It becomes a bug when events do not happen in the order the programmer intended.
The term originates with the idea of two signals racing each other to influence the output first.
[~/ruby/threads(master)]$ cat race_condition.rb def inc(n) n+1 end sum = 0 threads = (1..10).map do Thread.new do 10_000.times do sum = inc(sum) end end end threads.each(&:join)
A Global Interpreter Lock (GIL) is a mutual exclusion lock held by a programming language interpreter thread to avoid sharing code that is not thread-safe with other threads.
Some language implementations that implement a Global Interpreter Lock are CPython, the most widely used implementation of Python, and Ruby MRI, the reference implementation of Ruby.
JVM-based equivalents of these languages (Jython and JRuby) do not use Global Interpreter Locks. JRuby is a Ruby implementation allowing us to natively scale our Ruby code across multiple cores. IronPython and IronRuby are implemented on top of Microsoft's Dynamic Language Runtime and also avoid using a GIL.
Also, Rubinius implements native operating system threads for concurrency and has no global interpreter lock (GIL).
[~/ruby/threads(master)]$ rvm list rvm rubies jruby-1.7.3 [ x86_64 ] =* ruby-2.0.0-p247 [ x86_64 ] ruby-2.1.0-preview1 [ x86_64 ] # => - current # =* - current && default # * - default [~/ruby/threads(master)]$ rvm use jruby-1.7.3 Using /Users/casiano/.rvm/gems/jruby-1.7.3 [~/ruby/threads(master)]$ ruby -v jruby 1.7.3 (1.9.3p385) 2013-04-17 fffffff on Java HotSpot(TM) 64-Bit Server VM 1.7.0_25-b15 [darwin-x86_64] [~/ruby/threads(master)]$ ruby race_condition.rb 28375 [~/ruby/threads(master)]$ ruby race_condition.rb 25464 [~/ruby/threads(master)]$ ruby race_condition.rb 23442
[~/ruby/threads(master)]$ rvm use 2.1.0-preview1 Using /Users/casiano/.rvm/gems/ruby-2.1.0-preview1 [~/ruby/threads(master)]$ ruby race_condition.rb 100000 [~/ruby/threads(master)]$ ruby race_condition.rb 100000 [~/ruby/threads(master)]$
We can use the built-in class Mutex to create synchronized regions—areas of code that only one thread may enter at a time.
We resolve problems like these by using a cooperative locking mechanism:
lock
that data
lock
is represented by a Mutex
(short for "mutual exclusion") object
lock
a Mutex,
you call its lock
method
unlock
method of the Mutex
The lock
method blocks when called on a Mutex that’s
already locked, and it does not return until the caller has
successfully obtained a lock
unlock
s the Mutex correctly,
no thread will see the data in an inconsistent state and we won’t
have problems like those we’ve described
lock
and unlock
methods explicitly, it is more common to use the synchronize
method and associate a block with it
synchronize
locks the Mutex, runs the code in the block, and then unlock
s the Mutex in an ensure
clause so that exceptions are properly handled
[~/ruby/threads(master)]$ cat mutex_synchronize.rb # Wait for all threads (other than the current thread and # main thread) to stop running. # Assumes that no new threads are started while waiting. def join_all main = Thread.main # The main thread current = Thread.current # The current thread all = Thread.list # All threads still running # Now call join on each thread all.each {|t| t.join unless t == current or t == main } end def inc(n) n+1 end sum = 0 mutex = Mutex.new threads = (1..10).map do Thread.new do 10_000.times do mutex.synchronize do sum = inc(sum) end end end end join_all p sum