Why is the << operation on an array in Ruby not atomic?
Actually using MRI (Matz's Ruby implementation) the GIL (Global Interpreter Lock) makes any pure C-function atomic.
Since Array#<<
is implemented as pure C-code in MRI, this operation will be atomic. But note this only applies to MRI. On JRuby this is not the case.
To completely understand what is going on I suggest you read these two articles, which explains everything very well:
Nobody Understands the GIL
Nobody Understands the GIL - part 2
If you have multiple threads accessing the same array, use Ruby's built-in Queue class. It nicely handles producers and consumers.
This is the example from the documentation:
require 'thread'queue = Queue.newproducer = Thread.new do 5.times do |i| sleep rand(i) # simulate expense queue << i puts "#{i} produced" endendconsumer = Thread.new do 5.times do |i| value = queue.pop sleep rand(i/2) # simulate expense puts "consumed #{value}" endendconsumer.join
array
is your program variable when you apply an operation like <<
to it. It happens in three-steps:
- The variable is first copied into a CPU register.
- The CPU performs computations.
- The CPU writes back the result to variable memory.
So this high-level single-operation is performed in three steps. In between these steps, due to thread-context switching, other thread may read the same (old) value of the variable. That's why it's not an atomic operation.