В Ruby этот код не является потоковым, если array
изменен многими потоками:
array = []
array << :foo # many threads can run this code
Почему операция <<
не безопасна для потоков?
В Ruby этот код не является потоковым, если array
изменен многими потоками:
array = []
array << :foo # many threads can run this code
Почему операция <<
не безопасна для потоков?
array
- это ваша программная переменная, когда вы применяете к ней операцию типа <<
. Это происходит в три этапа:
Таким образом, эта одноуровневая операция высокого уровня выполняется в три этапа. В промежутке между этими шагами, из-за переключения потоков, другой поток может читать одно и то же (старое) значение переменной. Вот почему это не атомная операция.
Если у вас есть несколько потоков, обращающихся к одному и тому же массиву, используйте Ruby встроенный класс Queue. Он прекрасно обрабатывает производителей и потребителей.
Это пример из документации:
require 'thread'
queue = Queue.new
producer = Thread.new do
5.times do |i|
sleep rand(i) # simulate expense
queue << i
puts "#{i} produced"
end
end
consumer = Thread.new do
5.times do |i|
value = queue.pop
sleep rand(i/2) # simulate expense
puts "consumed #{value}"
end
end
consumer.join
Фактически, используя MRI (реализация Matz Ruby), GIL (Global Interpreter Lock) делает любую чистую C-функцию атомной.
Так как Array#<<
реализован как чистый C-код в MRI, эта операция будет атомарной. Но обратите внимание, что это относится только к МРТ. На JRuby это не так.
Чтобы полностью понять, что происходит, я предлагаю вам прочитать эти две статьи, которые объясняют все очень хорошо:
Эта ссылка может быть полезна для вас:
http://www.jstorimer.com/pages/ruby-core-classes-arent-thread-safe
Также вам может быть интересен этот камень:
Поскольку Ruby является языком с очень высоким уровнем, на уровне ОС на самом деле ничего не происходит. Только очень простые операции сборки являются атомарными на уровне ОС (зависит от ОС), и каждая операция Ruby даже простая 1 + 1
соответствует сотням или тысячам инструкций по сборке, таких как поиск методов, сбор мусора, инициализация объектов, вычисление областей и т.д.
Если вам нужно сделать операции атомарными, используйте мьютексы.
Простое отключение @Linuxios и @TheTinMan: операции высокого уровня (HLL) вообще не являются атомарными. Атомность (как правило) не проблема в однопоточных программах. В многопоточных программах вы (программист) должны рассуждать об этом с гораздо большей детализацией, чем одна операция HLL, поэтому отдельные операции HLL, которые являются атомарными, на самом деле вам не помогают. С другой стороны, хотя при выполнении операции HLL атома занимает всего несколько машинных команд до и после, по крайней мере, на современном оборудовании, статические (двоичный размер) и динамические (время выполнения) накладные расходы складываются. Хуже того, явная атомарность в значительной степени отключает всю оптимизацию, поскольку компиляторы не могут перемещать инструкции по атомным операциям. Нет реальной выгоды + значительная стоимость = не стартер.