Как функция C ++ сравнить_exchange определяет условия гонки? - PullRequest
0 голосов
/ 20 октября 2018

Как мы знаем, compare_exchange_weak() возвращает ошибку (ложное значение), если есть условие гонки, поэтому операция не может быть полностью выполнена.Но как именно состояние гонки определяется compare_exchange_weak()?

Команда lock cmpxchg возвращает ошибку, если более чем один поток пытается прочитать / записать значение, т.е. получить блокировку и именно таким образом compare_exchange_weak определяетсостояние гонки?

Ответы [ 2 ]

0 голосов
/ 22 октября 2018

lock cmpxchg является атомным;во время его выполнения ничего не может произойти (во всяком случае, логически).В отличие от реализации LL / SC compare_exchange_weak, он не может внезапно завершиться сбоем, когда другой поток пишет в той же строке кэша, только если сравнение на самом деле не удается.( Может ли CAS дать сбой для всех потоков? )

compare_exchange_strong может быть реализовано как lock cmpxchg без цикла.


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

В этомв случае да, причиной сбоя cmpxchg будет гонка с другим потоком.

Как объяснил @ ネ ロ ク, вы проверяете результат флага cmpxchg, чтобы получить логический результат compare_exchange_strong, т.е.скажите, не прошла ли часть сравнения и было ли выполнено сохранение или нет.См. Ручной ввод Intel для cmpxchg.


Существуют и другие варианты использования для lock cmpxchg, например, ожидание появления спин-блокировки при вращении на lock.compare_exchange_weak(0, 1), неоднократно пытаясь перевести разблокированную переменную в заблокированное состояние.Таким образом, неисправный CAS не из состояния гонки, он просто из замка все еще удерживается.(Поскольку вы передаете константу как «ожидаемую», а не то, что вы только что прочитали из этого места.)

Как правило, это не очень хорошая идея, AFAIK.Я думаю, что обычно лучше вращать только для чтения в ожидании блокировки, и пытаться взять блокировку только с помощью CAS или XCHG, если вы видели, что она была доступна.(Использование xchg и проверка целочисленного результата потенциально дешевле, чем lock cmpxchg, возможно, удерживая строку кэша заблокированной на меньшее количество циклов. Блокирует манипулирование памятью с помощью встроенной сборки )


TL: DR: программирование без блокировки требует прецизионного языка, если вы собираетесь рассуждать о корректности.

cmpxchg завершается неудачно и очищает ZF (создавая условие ne не равное), если EAX не соответствуетзначение в памяти, когда оно выполняется.Сама инструкция не знает о гонках и не заботится о них, только о точном состоянии, когда она сама выполняется.

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

0 голосов
/ 21 октября 2018

Инструкция cmpxchg влияет на флаг ZF: он устанавливается, если обмен завершился успешно, и очищается в противном случае.

Давайте рассмотрим это на примере:

std::atomic<int> a;

bool my_compare_exchange(int expected, int desired) {
   bool succeeded = a.compare_exchange_weak(expected, desired);
   return succeeded;
}

Функция my_compare_exchange() переводится в следующий код сборки:

my_compare_exchange:
        mov     eax, edi
        lock cmpxchg    DWORD PTR a[rip], esi
        sete    al // <-- conditional instruction
        ret

Регистр al устанавливается на 1 с использованием sete al, если обмен выполнен успешно (т. Е. ZF был установлен с помощьюcmpxchg).В противном случае он устанавливается на ноль (т. Е. ZF был очищен cmpxchg).

...