В Ruby этот код не является потокобезопасным, если array
изменяется многими потоками:
array = []
array << :foo # many threads can run this code
Почему операция <<
не является потокобезопасной?
В Ruby этот код не является потокобезопасным, если array
изменяется многими потоками:
array = []
array << :foo # many threads can run this code
Почему операция <<
не является потокобезопасной?
Фактически, используя MRI (реализация Matz Ruby), GIL (Global Interpreter Lock) делает любую чистую C-функцию атомарной.
Поскольку Array#<<
реализован в MRI как чистый C-код, эта операция будет атомарной. Но учтите, что это относится только к МРТ. На JRuby это не так.
Чтобы полностью понять, что происходит, я предлагаю вам прочитать эти две статьи, в которых все очень хорошо объясняется:
Никто не понимает GIL
Никто не понимает GIL - часть 2
Если у вас есть несколько потоков, обращающихся к одному и тому же массиву, используйте встроенный 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
array
- это переменная вашей программы, когда вы применяете к ней операцию типа <<
. Это происходит в три этапа:
Таким образом, эта высокоуровневая однократная операция выполняется в три этапа. Между этими шагами из-за переключения контекста потока другой поток может прочитать то же (старое) значение переменной. Вот почему это не атомарная операция.
Поскольку Ruby - это язык очень высокого уровня, на уровне ОС нет ничего атомарного. Только очень простые операции сборки являются атомарными на уровне ОС (зависят от ОС), и каждая операция Ruby, даже простая 1 + 1
, соответствует сотням или тысячам выполненных инструкций сборки, таких как поиск методов, сборка мусора, инициализация объекта, вычисления области и т. Д. .
Если вам нужно сделать операции атомарными, используйте мьютексы.
Просто копирование @Linuxios и @TheTinMan: операции языка высокого уровня (HLL) в целом не атомарны. Атомарность (как правило) не является проблемой для однопоточных программ. В многопоточных программах вы (программист) должны рассуждать об этом с гораздо большей степенью детализации, чем отдельная операция HLL, поэтому наличие отдельных операций HLL, которые являются атомарными, на самом деле вам не так сильно. С другой стороны, хотя создание атомарной операции HLL требует всего нескольких машинных инструкций до и после, по крайней мере, на современном оборудовании, статические (двоичный размер) и динамические (время выполнения) накладные расходы складываются. Хуже того, явная атомарность в значительной степени отключает всю оптимизацию, потому что компиляторы не могут перемещать инструкции между атомарными операциями. Никакой реальной выгоды + значительные затраты = не началось.