Рубин простой вопрос о состоянии гонки - PullRequest
5 голосов
/ 16 ноября 2010

Я сталкивался с этим примером условия гонки:

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)
p sum

Потоки работают в pararell, и есть вероятность, что, пока один поток читает значение суммы, другой завершает его приращение, но первый собираетсязакончите свое собственное увеличение со старым значением, и в результате сумма не изменится.

Но мне было интересно, почему, когда я заменяю строку «sum = inc (sum)» на «sum + = 1», вывод кажется всегда правильным.

Почему это так??

Это потому, что накладные расходы на вызов метода настолько велики по сравнению с простым присваиванием переменной, и, следовательно, некоторые потоки «не синхронизируются», что приводит к неправильному выводу?Предположим, что даже с прямой суммой + = 1 я все еще смог бы наблюдать состояние гонки, но только если бы я делал намного более длинный цикл суммирования и т. д .?

1 Ответ

3 голосов
/ 16 ноября 2010

Это потому, что накладные расходы на вызов метода настолько велики по сравнению с просто выполнением присваивания переменной, и, таким образом, некоторые потоки «выходят из синхронизации», вызывая неправильный вывод?

Да. Чтобы проверить это, просто увеличьте счетчик и запустите несколько тестов. Я увеличил его до 100_000.times и вот результаты:

$ seq 5 | xargs -L 1 ruby a.rb 100000
451167
472581
464413
442191
454204

Ну, это не выглядит так мило, правда?

Так что, да, инкремент не является атомарным в Ruby (и я сомневаюсь, что есть много языков, где он есть). Но есть вспомогательные классы для реализации такого поведения; например, этот .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...