Ошибки согласованности памяти и помехи потоков - PullRequest
15 голосов
/ 03 сентября 2010

В чем разница между ошибками согласованности памяти и интерференцией потоков? Чем отличается использование синхронизации, чтобы избежать их или нет? Пожалуйста, проиллюстрируйте пример. Я не мог получить это из урока Sun Java. Любая рекомендация по чтению материала (ов), чтобы понять это исключительно в контексте Java, будет полезна.

Ответы [ 6 ]

16 голосов
/ 03 сентября 2010

Ошибки согласованности памяти не могут быть поняты исключительно в контексте Java - детали поведения совместно используемой памяти в системах с несколькими процессорами сильно зависят от архитектуры, и, что еще хуже, x86 (где большинство людей, которые сегодня программируют, научились кодировать), имеет довольно дружественную к программисту семантику по сравнению с архитектурами, которые были спроектированы для многопроцессорных машин с самого начала (например, POWER и SPARC), поэтому большинство людей действительно не привыкли думать о семантике доступа к памяти,

Я приведу типичный пример того, как ошибки согласованности памяти могут привести к неприятностям.Предположим для этого примера, что начальное значение x равно 3. Почти все архитектуры гарантируют, что если один процессор выполняет код:

STORE 4 -> x     // x is a memory address
STORE 5 -> x 

, а другой процессор выполняет

LOAD x
LOAD x

либо увидит 3,3, 3,4, 4,4, 4,5, либо 5,5 с точки зрения двух LOAD инструкций.По сути, процессоры гарантируют, что порядок записи в одну область памяти поддерживается с точки зрения всех процессоров, даже если разрешено варьировать точное время, когда каждая из записей становится известной другим процессорам.

ГдеПроцессоры отличаются друг от друга, как правило, гарантиями, которые они выполняют около LOAD и STORE операций с различными адресами памяти.Предположим для этого примера, что начальные значения x и y равны 4.

STORE 5 -> x   // x is a memory address
STORE 5 -> y // y is a different memory address

, тогда другой процессор выполняет

LOAD x
LOAD y

В этом примере на некоторых архитектурахвторая нить может видеть 4,4, 5,5, 4,5, ИЛИ 5,4.Ой!

Большинство архитектур имеют дело с памятью с гранулярностью 32- или 64-битного слова - это означает, что на 32-битной машине POWER / SPARC вы не можете обновить 64-битное целочисленное расположение памяти исмело читайте его из другого потока ever без явной синхронизации.Гуфи, да?

Помехи от нитей намного проще.Основная идея заключается в том, что Java не гарантирует, что один оператор кода Java выполняется атомарно.Например, для увеличения значения необходимо прочитать значение, увеличить его, а затем снова сохранить.Таким образом, вы можете получить int x = 1 после того, как два потока выполнят x++, x может закончиться как 2 или 3 в зависимости от того, как чередуется код нижнего уровня (предположительно, выглядит абстрактный код нижнего уровня при работе здесь)как LOAD x, INCREMENT, STORE x).Основная идея здесь заключается в том, что Java-код разбит на более мелкие атомарные части, и вы не сможете делать предположения о том, как они чередуются, если вы явно не используете примитивы синхронизации.

Для получения дополнительной информации Проверьте эту бумагу.Это долго и сухо и написано пресловутым мудаком, но эй, это тоже довольно хорошо.Также проверьте это (или просто Google для "двойной проверки блокировки сломана").Эти проблемы с переупорядочением памяти подняли свои уродливые головы для многих программистов на C ++ / java, которые несколько лет назад пытались стать слишком умными со своими одноэлементными инициализациями.

4 голосов
/ 04 декабря 2014

Поток помех связан с тем, что потоки перезаписывают операторы друг друга (скажем, поток A увеличивает счетчик, а поток B уменьшает его одновременно ), что приводит к ситуации, когда фактическое значение счетчика непредсказуемо. Вы избегаете их, применяя монопольный доступ по одному потоку за раз.

С другой стороны, несоответствие памяти связано с видимостью. Поток A может увеличивать counter, но тогда поток B может не знать об этом изменении , пока , поэтому он может прочитать некоторое предыдущее значение. Вы избегаете их, устанавливая отношения «до того, как это произошло», то есть

- это просто гарантия того, что записи памяти одним конкретным оператором видны другому конкретному оператору (за Oracle )

2 голосов
/ 03 сентября 2010

Статья на эту тему «Модели памяти: случай переосмысления параллельных языков и аппаратного обеспечения» Адве и Бома в август 2010 г., том. 53 номер 8 выпуск связи АСМ. Это доступно в Интернете для членов Ассоциации компьютерного оборудования (http://www.acm.org).). Здесь рассматривается проблема в целом, а также обсуждается модель памяти Java.

Для получения дополнительной информации о модели памяти Java см. http://www.cs.umd.edu/~pugh/java/memoryModel/

0 голосов
/ 26 июня 2017

Во-первых, обратите внимание, что ваш источник НЕ лучшее место для изучения того, что вы пытаетесь выучить.Вы будете хорошо читать статьи из ответа @blucz (а также его ответа в целом), даже если это выходит за рамки Java.Oracle Trails не являются плохими сами по себе, но они упрощают вопросы и затушевывают их, следовательно, вы можете обнаружить, что не понимаете, что вы только что узнали, или полезно это или нет, и сколько.

Теперь, пытаясь ответить в основном в контексте Java.

Вмешательство потока

происходит, когда операции потока чередуются , то есть смешиваются.Нам нужны два исполнителя (темы) и общие данные (место, чтобы вмешиваться).Изображение Daniel Stori, с сайта turnoff.us:

Daniel Stori, turnoff.us

На рисунке вы видите, что два потока в процессе GNU / Linux могут мешать каждомуДругой.Потоки Java - это, по сути, объекты Java, указывающие на собственные потоки, и они также могут создавать помехи друг другу, если работают с одними и теми же данными (например, здесь, где «Рик» портит данные - рисование - своего младшего брата).

Ошибки непротиворечивости памяти - MCE

Важнейшими моментами здесь являются видимость памяти, события до и, вызванные @blucz, аппаратными средствами.

MCE - это, очевидно, ситуации, когда память становится несовместимой.Что на самом деле является термином для людей - для компьютеров память постоянна во всех случаях (если она не повреждена).«Несоответствия» - это то, что люди «видят», потому что они не понимают, что именно произошло, и ожидали чего-то другого.«Почему это 1? Это должно быть 2?!»

Эта «кажущаяся непоследовательность», этот «пробел» относится к памяти видимость , то есть к каким разным потокам смотрите когда они смотрят на память.И поэтому, что эти потоки работают на .Видите ли, в то время как чтение и запись в память линейны, когда мы размышляем о коде (особенно когда думаем о том, как он выполняется построчно) ... на самом деле это не так.Особенно, когда речь идет о потоках.Итак, в учебнике, который вы прочитали, приведен пример увеличения счетчика на два потока и того, как поток 2 считывает то же значение, что и поток 1. Фактические причины несоответствий памяти могут быть связаны с оптимизацией кода, с помощью javac, JIT или аппаратного обеспечения.модель согласованности памяти (то есть что-то, что люди CPU сделали для ускорения своего CPU и повышения его эффективности).Эти оптимизации включают в себя предварительные хранилища, прогноз ветвления, и на данный момент вы можете думать о них как о переупорядочении кода, так что в итоге он будет работать быстрее и потреблять / тратить меньше процессорных циклов.Однако, чтобы убедиться, что оптимизация не выходит из-под контроля (или слишком далеко), сделаны некоторые гарантии.Эти гарантии формируют отношения «происходит до», где мы можем сказать, что до до этой точки и после, вещи «произошли до».Представьте, что вы устраиваете вечеринку и вспоминаете, что Том пришел сюда ДО Сьюзи, потому что вы знаете, что Роб пришел после Тома и до Сьюзи.Роб - это событие, которое вы используете, чтобы сформировать отношения до и после того, как придут события Тома / Сьюзи.

https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/package-summary.html#MemoryVisibility

Ссылка выше расскажет вам больше о видимости памяти и о том, что происходит.до отношений в Java.Это не будет сюрпризом, но:

  1. синхронизация
  2. запускает поток
  3. присоединение к потоку
  4. volatile ключевое слово говорит вам, чтозапись происходит до последующих чтений, то есть чтение ПОСЛЕ записи не будет переупорядочено, чтобы быть записью «до», поскольку это нарушит отношения «происходит до».

Поскольку все, что касается памятиаппаратное обеспечение имеет важное значение.Ваша платформа имеет свои собственные правила, и хотя JVM пытается сделать их универсальными, заставляя все платформы вести себя одинаково, только это означает, что на платформе A будет больше барьеров памяти, чем на платформе B.

Ваши вопросы

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

Взаимодействие потоков связано с наложением, смешиванием, перемежением и касанием общих данных потоковых операций, вкручиванием их в процесс, что может привести к тому, что поток A получит хороший рисунок, уничтоженный потоком B.Вредный обычно помечает критический раздел, поэтому синхронизация работает.

Как отличается использование синхронизации, чтобы избежать их или нет?

Пожалуйста, прочитайте также о тонких замках, толстых замках и конфликтах нитей.Синхронизация, чтобы избежать вмешательства потока, делает это, заставляя только один поток обращаться к критическому разделу, другой поток блокируется (дорого, конфликт потоков).Когда дело доходит до MCE, синхронизация устанавливает, что происходит раньше, когда дело доходит до блокировки и разблокировки мьютекса, см. Более раннюю ссылку на описание пакета java.util.concurrent.

Для примеров: см. Оба предыдущих раздела.

0 голосов
/ 03 октября 2015

1. Интерференция потока

class Counter {
    private int c = 0;

    public void increment() {
        c++;
    }

    public void decrement() {
        c--;
    }

    public int value() {
        return c;
    }

}

Предположим, что есть две нити, Thread-A и Thread-B, работающие на тот же встречный экземпляр. Скажем, Thread-A вызывает increment (), и на в то же время Thread-B вызывает декремент (). Поток-A читает значение c и увеличить его на 1. В то же время Thread-B читает значение ( который равен 0, потому что увеличенное значение еще не установлено Thread-A), уменьшает его и устанавливает -1. Теперь Thread-A устанавливает значение 1.

2. Ошибки согласованности памяти

Ошибки согласованности памяти возникают, когда разные потоки имеют противоречивые представления об общих данных. В приведенном выше классе счетчик, Допустим, есть два потока, работающих на одном экземпляре счетчика, вызывает метод приращения, чтобы увеличить значение счетчика на 1. Вот нет никакой гарантии, что изменения, сделанные одним потоком, видны другой.

Для дальнейшего посещения это .

0 голосов
/ 03 сентября 2010

Проблемы непротиворечивости памяти обычно проявляются в виде разорванных связей «до того».

Time A: Thread 1 sets int i = 1
Time B: Thread 2 sets i = 2
Time C: Thread 1 reads i, but still sees a value of 1, because of any number of reasons that it did not get the most recent stored value in memory.

Вы можете предотвратить это, используя ключевое слово volatile для переменной или используя классы AtomicX изjava.util.concurrent.atomic пакет.Любое из этих сообщений гарантирует, что никакой второй поток не увидит частично измененное значение, и никто никогда не увидит значение, которое не является самым последним действительным значением в памяти.

(Синхронизация получателя и установщика будеттакже исправить проблему, но может показаться странным для других программистов, которые не знают, почему вы это сделали, а также могут сломаться перед лицом таких вещей, как связывающие и постоянные среды, использующие отражение.)

--

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

У нас есть объект PurchaseOrder с itemQuantity и itemPrice, автоматическая логика генерирует сумму счета.

Time 0: Thread 1 sets itemQuantity 50
Time 1: Thread 2 sets itemQuantity 100
Time 2: Thread 1 sets itemPrice 2.50, invoice total is calculated $250
Time 3: Thread 2 sets itemPrice 3, invoice total is calculated at $300

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

Вы решаете эту проблему либо с помощью ключевого слова synchronized, чтобы убедиться, что только один человекможет выполнять весь процесс одновременно или поочередно с блокировкой fиз пакета java.util.concurrent.locks.Использование java.util.concurrent обычно является предпочтительным подходом для новых программ.

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