Блокированные функции и атомарность: все еще запутано

Я все еще запутался в чтении и записи переменных атомарно. Так жаль заранее тех, кто пытался помочь в предыдущих вопросах.

Сегодня мне сказали, что нет необходимости в вызове функции Interlocked при чтении и записи 32-битного значения, которое удаляет мои предыдущие убеждения из windows. Действительно, это подтверждает то, что MSDN говорит

Простые чтения и записи для правильно выровненных 32-битных переменных являются атомарными операциями. Другими словами, вы не получите только одну часть обновленной переменной; все биты обновляются в атомном режиме

Поэтому я тогда посмотрел, что atomic_long в VS2017; его operator= выполняет _InterlockedExchange() в текущем значении с новым значением.

  1. Разве это не противоречит MSDN; если чтение / запись является атомарным, зачем нужна эта функция блокировки?

    На той же странице MSDN мы также имеем

    Простые чтения и записи для правильно выровненных 64-битных переменных являются атомарными в 64-битной Windows. Чтение и запись в 64-битные значения не гарантируют атомарность в 32-разрядной Windows. Считывание и запись в переменные других размеров не гарантируется атомарным на любой платформе.

    Поэтому я затем atomic_bool к operator=operator= ; он выполняет вызов _InterlockedExchange8 который MSDN говорит мне, доступен только для Windows 8 и выше.

    Это, похоже, поддерживает вторую цитату MSDN.

  2. Если я использую VS 2005 и ориентирован на Windows XP, как бы я мог бы обеспечить атомную чтение / запись логической переменной? Должен ли я использовать мьютекс или подобное?

  3. Если операция чтения / записи не является атомарной, это оставляет ее восприимчивой к разрыву; это верно?

  4. Я прочитал этот PAQ, в котором говорится, что примитивные типы не являются атомарными. Опять же это противоречит тому, что мне было сказано в моих вопросах, и о том, что сказал мне MSDN. Кто прав?

Ну, вы цитировали выдержку из документации, но не предложение, следующее за ней:

Однако синхронизация не гарантируется. Если два streamа считывают и записывают из одной и той же переменной, вы не можете определить, будет ли один stream выполнять свою операцию чтения до того, как другой выполнит операцию записи.

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

Функции блокировки гарантируют, что операция выполняется «транзакционным» образом, то есть она будет атомарной, и все streamи / процессоры будут получать последние обновленные значения, иначе это не гарантируется из-за кэширования (каждый процессор / kernel может хранить копию в своем локальном кеше). Функции блокировки, а также все функции синхронизации обеспечивают синхронизацию содержимого кеша.

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

Сегодня мне сказали, что нет необходимости в вызове функции блокировки при чтении и записи 32-битного значения

Потому что это неправда, по крайней мере, не такая общая.

Применяется только к x86, и только если 32-битный тип значения выровнен, и эта переменная фактически используется в памяти с использованием одной 32-битной инструкции. Обычно это происходит, но ни в коем случае не гарантируется, ни выравнивание, ни атомный магазин, как компилятор, не могут решить оптимизировать или не могут выполнить выравнивание и для этого должны разделиться.

Разве это не противоречит MSDN; если чтение / запись является атомарным, зачем нужна эта функция блокировки?

atomic_long не выровнен по определению, поэтому для формально правильной функции требуется атомный обмен.

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

И это другая важная часть об атоматике – они образуют барьер памяти.

Это означает, что ни компилятор, ни процессор не могут повторно заказать доступ к памяти. Если бы у вас было, например, две простые переменные int которые вы писали независимо друг от друга в одной и той же области, компилятор был бы свободен для пакетной или переупорядочивать записи по своему усмотрению.

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

Это выходит за frameworks простого volatile что также препятствует переупорядочению доступа к памяти, но все равно может потратить несколько микросекунд, ожидая, что записи в память будут сброшены на другое kernel.

Если я использую VS 2005 и ориентирован на Windows XP, как бы я мог бы обеспечить атомную чтение / запись логической переменной? Должен ли я использовать мьютекс или подобное?

Да. Ну, запись является атомарной, в любом случае, но вам действительно нужен барьер памяти, если вы используете логическое значение, чтобы сигнализировать, что другая область памяти теперь безопасна для доступа (например, spinlock).

Если операция чтения / записи не является атомарной, это оставляет ее восприимчивой к разрыву; это верно?

Не для bool , поскольку это действительно только один байт, и я не знаю ни одной архитектуры, которая могла бы разрывать переменную на уровне подбайта.

Для всех более крупных типов, да, поскольку aligment не гарантируется. И, как говорилось ранее, у вас нет гарантий, что компилятор использовал атомную инструкцию, даже если она была «бесплатно».