Согласно этому сообщению, i += 1
является потокобезопасным в МРТ
Не совсем. В сообщении блога говорится, что вызовы методов эффективно поддерживают потоки в MRI.
Сокращенное присвоение i += 1
является синтаксическим сахаром для:
i = i + 1
Итак, у нас есть присваивание i = ...
и вызова метода i + 1
. Согласно сообщению в блоге, последний является потокобезопасным. Но это также говорит о том, что переключение потоков может произойти непосредственно перед возвратом результата метода, то есть до того, как результат будет переназначен на i
:
i = i + 1
# ^
# here
К сожалению, это не так просто, продемонстрируйте это в Ruby.
Однако мы можем подключиться к Integer#+
и произвольно попросить планировщик потока передать управление другому потоку:
module Mayhem
def +(other)
Thread.pass if rand < 0.5
super
end
end
Если MRI обеспечивает безопасность потока для всего оператора i += 1
, вышеприведенное не должно иметь никакого эффекта. Но это так:
Integer.prepend(Mayhem)
10.times do
i = 0
Array.new(10) { Thread.new { i += 1 } }.each(&:join)
puts i
end
Выход:
5
7
6
4
4
8
4
5
6
7
Если вы хотите поточно-ориентированный код, не полагайтесь на детали реализации (они могут измениться). В приведенном выше примере вы можете обернуть чувствительную часть в вызов Mutex#synchronize
:
Integer.prepend(Mayhem)
m = Mutex.new
10.times do
i = 0
Array.new(10) { Thread.new { m.synchronize { i += 1 } } }.each(&:join)
puts i
end
Выход:
10
10
10
10
10
10
10
10
10
10