std:: atomic: разница между x.fetch add (1) и x++;
в чем разница между
extern std::atomic<int> x;
int i = x++;
и
extern std::atomic<int> x;
int i = x.fetch_add(1);
Я чувствую, что вторая версия безопаснее, но я не видел никаких различий в тестировании между этими двумя версиями.
2 ответов
разница определенно не в безопасности = атомарности, которая гарантирована для обоих методов.
самое важное различие, я думаю, что fetch_add()
может принимать другой аргумент порядка памяти, в то время как для оператора инкремента он всегда memory_order_seq_cst
.
еще одно очевидное различие заключается в том, что fetch_add()
можно взять не только 1
в качестве аргумента, а с другой стороны, operator++
, скорее всего, будет реализован с помощью lock inc
инструкция (хотя, теоретически ничто не мешает компилятору такой оптимизации для fetch_add(1)
а)
таким образом, отвечая на ваш точный вопрос, нет никакой семантически важной разницы между x++
и x.fetch_add(1)
. The док говорит:
эта функция ведет себя так, как если бы atomic::fetch_add был вызван с 1 и memory_order_seq_cst в качестве аргументов.
C++11
на в C++11 N3337 проект 29.6.5 / 33 "требования к операциям с атомными типами":
C A ::operator++(int) volatile noexcept;
C A ::operator++(int) noexcept;
возвращает: fetch_add(1)
29.6.5 / 2 уточняет C
и A
:
- A относится к одному из атомарных типов.
- в C относится к соответствующему неатомарный типа
Я не смог найти это объясняло ясно, но я полагаю Returns: fetch_add(1)
означает, что fetch_add(1)
вызвано для своего побочного эффекта конечно.
также стоит посмотреть на версию префикса немного дальше:
C A ::operator++() volatile noexcept;
C A ::operator++() noexcept;
эффекты: fetch_add(1)
возвращает: fetch_add(1) + 1
что означает, что этот возвращает значение + 1, как обычный префикс инкремента для целых чисел.
GCC 4.8
libstdc++ - v3 / include/std / atomic говорит atomic<int>
наследует __atomic_base<int>
:
struct atomic<int> : __atomic_base<int>
libstdc++ - v3 / include/bits / atomic_base.h реализует его как:
__int_type
operator++(int) noexcept
{ return fetch_add(1); }
__int_type
operator++(int) volatile noexcept
{ return fetch_add(1); }
__int_type
operator++() noexcept
{ return __atomic_add_fetch(&_M_i, 1, memory_order_seq_cst); }
__int_type
operator++() volatile noexcept
{ return __atomic_add_fetch(&_M_i, 1, memory_order_seq_cst); }
_GLIBCXX_ALWAYS_INLINE __int_type
fetch_add(__int_type __i,
memory_order __m = memory_order_seq_cst) noexcept
{ return __atomic_fetch_add(&_M_i, __i, __m); }
_GLIBCXX_ALWAYS_INLINE __int_type
fetch_add(__int_type __i,
memory_order __m = memory_order_seq_cst) volatile noexcept
{ return __atomic_fetch_add(&_M_i, __i, __m); }
Я не понимаю, почему постфикс называет fetch_add
helper и префикс используют встроенный непосредственно, но в конце концов все они сводятся к GCC intrinsics __atomic_fetch_add
и __atomic_add_fetch
которые делают реальную работу.