2013-11-25 3 views
1

я читал книгу под названием искусство многопроцессорной программирования и наткнулся на такие функции, как получить(), getandset(), compareandset(), getandIncrease(), getandIncrease() и т.д.Как функция становится атомарной?

В книге говорится, что все вышеприведенные функции являются атомарными, и я согласен, но у меня были свои сомнения в том, как какая-то функция становится атомной функцией.

Почему функция получить или сравнить стать атомными? - потому что он должен ждать, пока он не получит значение, или ждет, пока какое-то условие станет истинным, что создает барьер, следовательно, атомный.

Я прав, думая так? есть ли что-то, что я пропустил?

Когда я

if (tail_index.get() == (head_index.getAndIncrement()) 

это атомное?

+3

Атомные средства «неделимые». Вы не можете видеть части атомной операции, все, что она делает, кажется, происходит сразу. В вашем случае нет ничего, чтобы ваши два метода get выполнялись как один, поэтому я бы не назвал его атомарным. –

ответ

1

Функция атом, если это происходит мгновенно. [1]

Здесь «кажется» означает с точки зрения остальной части системы. Например, рассмотрим синхронизированную функцию, которая меняет связанный список. Внешнему наблюдателю операция явно не происходит мгновенно: для обновления всех указателей списка требуется много чтений и записей. Однако, поскольку блокировка сохраняется все время, никакая другая часть системы не может прочитать список за это время, поэтому для них обновление появляется мгновенно.

Аналогично, операции CAS (сравнение и набор) не фактически происходят мгновенно на современных компьютерах. Требуется время для одного ядра ЦП для получения исключительного права на запись в значение, а затем для получения нового значения для другого ядра требуется дополнительное время для повторного получения доступа к чтению, чтобы увидеть новое значение. В течение этого времени CPU выполняет другие команды параллельно.Чтобы гарантировать иллюзию мгновенного выполнения, JVM выдает инструкции ЦП до и после операции CAS, чтобы гарантировать, что логически последующие чтения не будут завершены и выполнены до завершения CAS (что позволит вам прочитать часть связанного списка до вы, например, сделали блокировку), и что логически предшествующие записи не задерживаются и не выполняются после завершения CAS (что позволило бы другому потоку блокировать до того, как связанный список был полностью обновлен).

Эти инструкции по заказу ЦП - это ключевое различие между AtomicInteger.compareAndSet и AtomicInteger.weakCompareAndSet (бит «может не срабатывать ложно» легко выпрямляется с помощью петли). Без гарантий упорядочения слабая операция CAS не может использоваться для реализации большинства параллельных алгоритмов и «редко является подходящей альтернативой compareAndSet».

Если это звучит сложно ... ну ... это! Вот почему you can still get a PhD by designing a concurrent algorithm. Чтобы показать правильность для параллельного алгоритма, вы должны подумать о том, что может сделать любой другой поток, чтобы помешать вам. Это может помочь, если вы думаете о них как о противниках, пытаясь сломать иллюзию атомарности. Например, давайте рассмотрим ваш пример:

if (tail_index.get() == (head_index.getAndIncrement())) 

Я предполагаю, что это является частью методы поп элемента от стека реализован как циклический массив со счетчиками индекса, и выполнить тело «если», если стек теперь пуст. Поскольку head_index и tail_index доступны отдельно, ваш противник может «разделить» их на столько операций, сколько ему нравится. (Предположим, например, что ваш поток прерывается ОС между get и getAndIncrement.) Таким образом, ему было бы легко добавить десятки элементов в стек, а затем удалить все, кроме одного, оставив head_index выше tail_index ; ваш блок if никогда не будет выполнен, даже если вы удаляете последний элемент в стеке.

Итак, когда ваша книга говорит, что get(), getAndSet() и т. Д. Являются атомарными, это не делает общее утверждение о возможной реализации этих методов. Это говорит вам, что стандарт Java гарантирует, что они являются атомарными и делает это, тщательно используя доступные инструкции ЦП, таким образом, что было бы невозможно сделать в простой Java (synchronized позволяет вам эмулировать его, но более дорогостоящим).

4

Метод сделан atomic относительно некоторого экземпляра путем добавления явной безопасности потоков. Во многих случаях это делается путем маркировки метода как synchronized. Магии нет, если вы посмотрите на исходный код потокобезопасного класса, который утверждает, что методы являются атомарными, вы увидите блокировку.

WRT для вашей второй части, нет, это не атомный. Каждый вызов метода является атомарным, но когда вы ставите два вместе, комбинация не является атомарной. get и getAndIncrement были явно сделаны атомарными. Когда вы добавляете другой код (или комбинацию вызовов), он не является атомарным, если вы не сделаете это так.

+0

спасибо за ответ Джон. Я все еще запутался в том, что, когда две атомные операции вместе, комбинация не является атомарной? это потому, что каждый из них происходит в отдельной ните атомизированной и свободной атомарности как дыра? как последовательная консистенция не являются композиционными? – solti

+0

Не потому, что каждый находится в отдельном потоке, даже один поток не является атомарным. Подумайте, 'get' входит и получает блокировку. Он освобождает замок перед выходом. Затем JVM может принимать дополнительные наборы (проверяя ||) перед вызовом 'getAndIncrement', который затем пытается получить блокировку. Каждый раз, когда происходит освобождение блокировки, прежде чем пытаться его раздобыть, есть потенциал для чего-то еще. –

+2

BTW Ни один из классов AtomicXxxx не использовал 'synchronized' или locks. –

1

Нет, функция, используя get() не является атомарной. Но, например, getAndIncrement или compareAndSet являются атомами. Это означает, что он гарантировал, что вся логика выполнена атомарно. Для get() есть еще одна уверенность: когда вы публикуете атомное значение в один поток, он сразу становится видимым для других потоков (подобно изменчивым полям). Энергонезависимые и неатомные значения dont: бывают случаи, когда значение, заданное для нелетучих значений, не видимо для других потоков; эти потоки получают значение поля значения старого значения.

Но вы всегда можете написать атомную функцию, используя классы Atomic* и другие примитивы синхронизации.