Атомарность оператора приращения

В интервью мне сказали, что в C использование оператора ++ (скажем, i ++) является атомарной операцией, а выполнение, скажем, «i + = 1» - нет. Я думал, что эти операции точно такие же, когда дело касается безопасности потоков или атомарности. Я что-то упустил или это совсем другое?


person samt1903    schedule 16.01.2015    source источник
comment
Интервьюер ошибается - ни то, ни другое не гарантирует атомарности.   -  person Paul R    schedule 16.01.2015
comment
Если вам об этом сказал потенциальный будущий начальник, я предлагаю вам найти другое место потенциальной работы.   -  person WhozCraig    schedule 16.01.2015
comment
Компилятор, скорее всего, будет реализовывать выражения ++i и i += 1 очень похоже (они одинаковы, потому что результатом выражения является увеличивающееся значение). Это отличается от i++, где результатом выражения является значение до приращения. Как полные утверждения, эти три эквивалентны. Ни один из них не может быть атомарным.   -  person Jonathan Leffler    schedule 16.01.2015
comment
См. Также В C i += 1 атомарно? Это не дубликат, потому что он не упоминает i++ или ++i, но это тесно связано.   -  person Jonathan Leffler    schedule 16.01.2015
comment
Это очень хороший пример того, почему C ++! = C. + 1 в вашем вопросе (не забудьте поставить -1 экзаменатору. Если вам не нужна работа;)).   -  person frasnian    schedule 16.01.2015


Ответы (3)


Ерунда. Любой из них может быть или не быть атомарным, в зависимости от типа данных, архитектуры и, возможно, компилятора (стандарт не дает гарантий относительно атомарности в целом, если вы не используете атомарность C11), но я не вижу ничего хорошая причина думать, что в общем случае i++ будет атомарным, а i += 1 - нет. Очень вероятно, что они фактически генерируют один и тот же код в контексте, где результат выражения не используется.

person hobbs    schedule 16.01.2015

Операторы * i++; и i += 1; эквивалентны в C. Ни один из них не может быть атомарным.

В частности, приращения переменных, превышающих размер системного слова (например, 64-разрядные переменные в 32-разрядных системах), почти всегда не атомарны, поскольку атомарное приращение такой переменной часто требует явной блокировки. Кроме того, некоторые архитектуры не поддерживают прямое увеличение переменных в памяти. (То есть они требуют явной последовательности загрузки / изменения / сохранения.) Эти архитектуры не могут атомарно изменять любую переменную без блокировки.

* при рассмотрении как отдельных операторов, а не выражений

person Community    schedule 16.01.2015
comment
дополнительная информация: ++i определяется как i += 1 - person M.M; 16.01.2015

интервьюер ошибается.

Я скомпилировал две функции с помощью gcc-4.8.2 -O2 -S

void increment1(int *a) {
  *a += 1;
}
void increment2(int *a) {
  (*a)++;
}

оба генерируют ровно the same сборки

increment1:
.LFB0:
        .cfi_startproc
        addl    $1, (%rdi)
        ret
        .cfi_endproc
.LFE0:
        .size   increment1, .-increment1
        .p2align 4,,15
        .globl  increment2
        .type   increment2, @function
increment2:
.LFB1:
        .cfi_startproc
        addl    $1, (%rdi)
        ret
        .cfi_endproc

Но в контексте более точного технического анализа, оба они atomic write, что означает, что он не дает результата MAD. если вы используете переменную int64_t в 32-разрядной или менее разрядной среде ЦП, 64-разрядная модификация приведет к многократной операции записи. Без замка не может быть atomic write.

Вы можете использовать операцию __sync_fetch_and_add(&a, 1) для атомарного приращения в среде gcc.

person Hiroki Kumazaki    schedule 16.01.2015
comment
Тот факт, что эти две функции генерируют идентичную сборку x86 с использованием этой версии GCC, не обязательно означает, что они всегда идентичны. Различные компиляторы и архитектуры могут вести себя по-разному. (Вы правы в том, что они идентичны, но только случайно.) - person ; 16.01.2015