Атомарные типы
Синтаксис
_Atomic ( type-name ) | (1) | (начиная с C11) |
_Atomic type-name | (2) | (начиная с C11) |
const, volatile и restrict, хотя, в отличие от других квалификаторов, атомарная версия type-name может иметь другой размер, выравнивание и представление объекта.| type-name | - | любой тип, кроме массива или функции. Для (1) type-name также не может быть атомарным или иметь квалификаторы cvr |
Заголовок <stdatomic.h> определяет множество удобных псевдонимов типов — от atomic_bool до atomic_uintmax_t, — упрощающих использование этого ключевого слова со встроенными типами и типами библиотеки.
_Atomic const int* p1; // p is a pointer to an atomic const int const atomic_int* p2; // same const _Atomic(int)* p3; // same
Если компилятор определяет макроконстанту __STDC_NO_ATOMICS__, ключевое слово _Atomic не предоставляется.
Пояснение
Объекты атомарных типов — единственные объекты, для которых не возникают гонки данных; то есть их можно изменять одновременно из двух потоков или изменять из одного потока и читать из другого.
У каждого атомарного объекта есть собственный порядок модификаций — полный порядок модификаций, выполненных для этого объекта. Если с точки зрения некоторого потока модификация A некоторого атомарного M происходит раньше модификации B того же атомарного M, то в порядке модификаций M A предшествует B.
Обратите внимание: хотя у каждого атомарного объекта есть собственный порядок модификаций, единого полного порядка не существует; разные потоки могут наблюдать модификации разных атомарных объектов в разном порядке.
Для всех атомарных операций гарантируются четыре вида когерентности:
-
когерентность записи-записи: если операция
A, изменяющая атомарный объектM, происходит раньше операцииB, изменяющейM, тоAрасполагается раньшеBв порядке модификацийM. -
когерентность чтения-чтения: если вычисление значения
Aатомарного объектаMпроисходит раньше вычисления значенияBобъектаM, аAполучает своё значение из побочного эффектаXнадM, то вычисленноеBзначение совпадает либо со значением, сохранённым операциейX, либо со значением, сохранённым побочным эффектомYнадM, причёмYрасполагается позжеXв порядке модификацийM. -
когерентность чтения-записи: если вычисление значения
Aатомарного объектаMпроисходит раньше операцииBнадM, тоAполучает своё значение из побочного эффектаXнадM, причёмXрасполагается раньшеBв порядке модификацийM. -
когерентность записи-чтения: если побочный эффект
Xнад атомарным объектомMпроисходит раньше вычисления значенияBобъектаM, то вычислениеBполучает своё значение изXлибо из побочного эффектаY, который располагается послеXв порядке модификацийM.
Некоторые атомарные операции также являются операциями синхронизации; они могут обладать дополнительной семантикой освобождения, захвата или последовательной согласованности. См. memory_order.
Встроенные операторы инкремента и декремента и составное присваивание являются атомарными операциями чтения-модификации-записи с полным последовательно согласованным порядком (как при использовании memory_order_seq_cst). Если требуется менее строгая семантика синхронизации, вместо них можно использовать функции стандартной библиотеки.
Атомарные свойства имеют смысл только для выражений lvalue. Преобразование lvalue в rvalue (моделирующее чтение памяти из атомарной области в регистр процессора) снимает атомарность наряду с другими квалификаторами.
Примечания
Доступ к члену атомарной структуры или объединения является неопределённым поведением.
Библиотечный тип sig_atomic_t не обеспечивает межпоточную синхронизацию или упорядочение памяти, а обеспечивает только атомарность.
Типы volatile не обеспечивают межпоточную синхронизацию, упорядочение памяти или атомарность.
Рекомендуется, чтобы реализации обеспечивали одинаковое представление _Atomic(T) в C и std::atomic<T> в C++ для всех возможных типов T. Механизмы обеспечения атомарности и упорядочения памяти должны быть совместимы.
Ключевые слова
Пример
#include <stdatomic.h>
#include <stdio.h>
#include <threads.h>
atomic_int acnt;
int cnt;
int f(void* thr_data)
{
for (int n = 0; n < 1000; ++n)
{
++cnt;
++acnt;
// for this example, relaxed memory order is sufficient, e.g.
// atomic_fetch_add_explicit(&acnt, 1, memory_order_relaxed);
}
return 0;
}
int main(void)
{
thrd_t thr[10];
for (int n = 0; n < 10; ++n)
thrd_create(&thr[n], f, NULL);
for (int n = 0; n < 10; ++n)
thrd_join(thr[n], NULL);
printf("The atomic counter is %u\n", acnt);
printf("The non-atomic counter is %u\n", cnt);
}
Возможный вывод:
The atomic counter is 10000 The non-atomic counter is 8644
Ссылки
- Стандарт C23 (ISO/IEC 9899:2024):
- 6.7.2.4 Спецификаторы атомарных типов (стр.: TBD)
- 7.17 Атомарные операции <stdatomic.h> (стр.: TBD)
- Стандарт C17 (ISO/IEC 9899:2018):
- 6.7.2.4 Спецификаторы атомарных типов (стр.: 87)
- 7.17 Атомарные операции <stdatomic.h> (стр.: 200–209)
- Стандарт C11 (ISO/IEC 9899:2011):
- 6.7.2.4 Спецификаторы атомарных типов (стр.: 121)
- 7.17 Атомарные операции <stdatomic.h> (стр.: 273–286)
См. также
| Библиотека поддержки многопоточности | |
Документация C++ для atomic |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/c/language/atomic