Когда следует использовать функцию Win32 InterlockedExchange?

Я наткнулся на функцию InterlockedExchange и мне было интересно, когда мне следует использовать эту функцию. На мой взгляд, установка 32-битного значения на процессоре x86 всегда должна быть атомарной?
В случае, когда я хочу использовать функцию, новое значение не зависит от старого значения (это не операция приращения). Не могли бы вы привести пример, когда этот метод является обязательным (я не ищу InterlockedCompareExchange)


person EFrank    schedule 16.10.2008    source источник


Ответы (7)


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

Механизмы синхронизации потоков обеспечивают синхронизацию между ядрами. Для получения дополнительной информации см. http://blogs.msdn.com/oldnewthing/archive/2008/10/03/8969397.aspx или Google для получения и выпуска семантики

person Nir    schedule 16.10.2008
comment
Тайники здесь второстепенны. Даже если кешей нет и процессоры общаются с памятью напрямую, нормальная последовательность чтения/записи не является атомарной. - person Suma; 08.06.2010

InterlockedExchange одновременно запись и чтение — возвращает предыдущее значение.

Это необходимо для того, чтобы другой поток не записал другое значение сразу после того, как это сделали вы. Например, скажем, вы пытаетесь увеличить переменную. Вы можете прочитать значение, добавить 1, а затем установить новое значение с помощью InterlockedExchange. Значение, возвращаемое InterlockedExchange, должно совпадать со значением, которое вы первоначально прочитали, иначе другой поток, вероятно, увеличил его в то же время, и вам нужно выполнить цикл и повторить попытку.

person Jason Cohen    schedule 16.10.2008
comment
В этом случае я бы использовал InterlockedCompareExchange, потому что проверка того, было ли значение по-прежнему исходным, выполняется уже методом. - person EFrank; 16.10.2008

Помимо записи нового значения, InterlockedExchange также считывает и возвращает предыдущее значение; вся эта операция атомарна. Это полезно для неблокирующих алгоритмов.

(Кстати, атомарность 32-разрядных операций записи не гарантируется. Рассмотрим, например, случай, когда запись не выровнена и пересекает границу кэша.)

person moonshadow    schedule 16.10.2008
comment
Это было бы атомарно с точки зрения программного обеспечения, работающего на процессоре, но не с точки зрения аппаратного обеспечения, находящегося на шине. - person Brian Knoblauch; 16.10.2008
comment
Будет ли это атомарным от точки зрения программного обеспечения, работающего на другом ядре? Как насчет записи, которая выходит за границу страницы и вызывает ошибку? Прошли годы с тех пор, как я занимался низкоуровневым кодированием x86, думаю, я могу лгать :/ - person moonshadow; 16.10.2008

Установка 32-битного значения является атомарной, но только если вы устанавливаете литерал.

b = a - это 2 операции:

mov         eax,dword ptr [a] 
mov         dword ptr [b],eax 

Теоретически может быть некоторый перерыв между первой и второй операцией.

person Gerald    schedule 16.10.2008
comment
А если будет какое-то прерывание, то какое - чуть дольше, но значение в eax не изменится. Настоящая суть InterlockedExchange в том, что это атомарное чтение и запись. - person Suma; 08.06.2010
comment
Тогда что? Ад. Прерывание может вызвать системный вызов, системный вызов переключения контекста, а затем другой поток изменяет значение. Затем EAX восстанавливается и значение снова меняется. Условия гонки особенно фатальны при подсчете ссылок. Один поток может увеличивать значение, в то время как другой поток уменьшает значение, или два потока могут увеличивать его одновременно. То, что EAX восстановлен, это не решение, а проблема. - person Andreas Spindler; 28.08.2013
comment
И становится еще хуже, когда используется более одного процессора/ядра. - person SamB; 02.05.2015
comment
В этом случае ничего серьезного. b будет записано одной инструкцией независимо от ее предыдущего значения. - person Fr0sT; 16.09.2016

Запись значения никогда не является атомарной по умолчанию. Когда вы записываете значение в переменную, генерируются несколько машинных инструкций. В современных вытесняющих ОС ОС может переключаться на другой поток между отдельными операциями записи.

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

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

person Mathieu Garstecki    schedule 16.10.2008
comment
-1, Неправильные записи с выравниванием по простому являются атомарными на x86. - person Johan; 20.09.2014

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

Таким образом, если «i» является целым числом, эти вызовы (взятые по отдельности) не нуждаются в InterlockedExchange вокруг «i»:

a = i;
i = 9;
i = a;
i = a + 9;
a = i + 9;
if(0 == i)

Ни одно из этих утверждений не опирается на ОБА начальное и конечное значения «i». Но эти следующие вызовы ДЕЙСТВИТЕЛЬНО нуждаются в InterlockedExchange вокруг 'i':

a = i++;  //a = InterlockedExchange(&i, i + 1);

Без этого два потока, выполняющиеся через один и тот же код, могут получить одно и то же значение «i», назначенное «a», или «a» может неожиданно пропустить два или более числа.

if(0 == i++) //if(0 == InterlockedExchange(&i, i + 1))

Два потока могут оба выполнять код, который должен произойти только один раз.
и т. д.

person helmk    schedule 12.05.2011
comment
Неверный пример a = InterlockedExchange(&i, i + 1);. Ваш i может быть изменен другим процессором между i + 1 и InterlockedExchange. - person Fr0sT; 16.09.2016

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

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

  1. 32-битные операции чтения и записи ЯВЛЯЮТСЯ атомарными, но в зависимости от вашего кода это может не иметь большого значения.
  2. не беспокойтесь о невыровненном чтении/записи. ВСЕ 32-разрядные операции записи в 32-разрядную переменную должны быть выровнены, иначе произойдет сбой страницы машины.
  3. не беспокойтесь о переносе записи в конце кэшированной страницы, этого не может быть.
  4. Если вам нужно писать и читать в одном потоке, а вы пишете в другом потоке, вам нужно использовать InterlockedExchange. Если вы просто читаете значение в одном потоке и записываете его в другом, то вам не нужно его использовать, но эти значения могут быть непостоянными из-за многопоточности.
person Ericf.    schedule 12.02.2014
comment
-1 32-битные переменные не нужно выравнивать на x86. И 32-битные записи являются атомарными для x86, но не для многих других архитектур. - person Johan; 20.09.2014