std::atomic::compare_exchange_weak, std::atomic::compare_exchange_strong
bool compare_exchange_weak( T& expected, T desired, std::memory_order success, std::memory_order failure ) noexcept; |
(1) | (начиная с C++11) |
bool compare_exchange_weak( T& expected, T desired, std::memory_order success, std::memory_order failure ) volatile noexcept; |
(2) | (начиная с C++11) |
bool compare_exchange_weak( T& expected, T desired, std::memory_order order = std::memory_order_seq_cst ) noexcept; |
(3) | (начиная с C++11) |
bool compare_exchange_weak( T& expected, T desired, std::memory_order order = std::memory_order_seq_cst ) volatile noexcept; |
(4) | (начиная с C++11) |
bool compare_exchange_strong( T& expected, T desired, std::memory_order success, std::memory_order failure ) noexcept; |
(5) | (начиная с C++11) |
bool compare_exchange_strong( T& expected, T desired, std::memory_order success, std::memory_order failure ) volatile noexcept; |
(6) | (начиная с C++11) |
bool compare_exchange_strong( T& expected, T desired, std::memory_order order = std::memory_order_seq_cst ) noexcept; |
(7) | (начиная с C++11) |
bool compare_exchange_strong ( T& expected, T desired, std::memory_order order = std::memory_order_seq_cst ) volatile noexcept; |
(8) | (начиная с C++11) |
Атомарно сравнивает представление объекта (до C++20)представление значения (начиная с C++20), хранящееся в *this, с expected, и, если они побитово равны, заменяет первое на desired (выполняет операцию чтение-изменение-запись). Иначе загружает фактическое значение, хранящееся в *this, в expected (выполняет операцию загрузки).
| Перегрузки | Модель памяти для | |
|---|---|---|
| операций чтения‑изменения‑записи | операций загрузки | |
| (1,2,5,6) | успех
|
неудача
|
| (3,4,7,8) | порядок
|
|
Если failure сильнее, чем success или (до C++17) является одним из std::memory_order_release и std::memory_order_acq_rel, поведение не определено.
|
Это устарело, если |
(начиная с C++20) |
Параметры
| expected | — | ссылка на значение, которое, как ожидается, будет найдено в атомарном объекте. |
| desired | — | значение для сохранения в атомарном объекте, если оно соответствует expected |
| success | — | модель синхронизации памяти для операции чтения-модификации-записи, если сравнение успешно. |
| failure | — | порядок синхронизации памяти для операции загрузки, если сравнение неудачное. |
| order | — | порядок синхронизации памяти для обеих операций |
Возвращаемое значение
true, если внутреннее значение атомарного объекта было изменено, иначе false.
Примечания
Сравнение и копирование происходят побитово (аналогично функциям std::memcmp и std::memcpy) без вызовов конструкторов, операторов присваивания или сравнения.
compare_exchange_weak может потерпеть неудачу ложно, то есть действовать, как если бы *this != expected, даже если они равны. Когда функция сравнения-и-обмена выполняется в цикле, compare_exchange_weak даст лучшую производительность на некоторых платформах.
Если для compare_exchange_weak требуется цикл, а для std::compare_exchange_strong нет, то предпочтительнее std::compare_exchange_strong, если только объектное представление T не может включать биты заполнения, (до C++20) биты-ловушки или иметь несколько представлений для одного и того же значения (например NaN для числа с плавающей запятой). В этих случаях обычно работает compare_exchange_weak, потому что оно быстро сходится к некоторому стабильному представлению объекта.
Для объединения с битами, которые участвуют в представлениях значений одних элементов, но не участвуют в других, сравнение и обмен всегда может завершиться неудачно, потому что такие биты заполнения имеют неопределённые значения, когда они не участвуют в представлении значения активного элемента.
|
Биты заполнения, которые никогда не участвуют в представлении значения объекта, игнорируются. |
(начиная с C++20) |
Примеры
Операции сравнения-и-обмена часто используются в качестве основных строительных блоков структур данных без блокировки.
#include <atomic>
template<typename T>
struct node
{
T data;
node* next;
node(const T& data) : data(data), next(nullptr) {}
};
template<typename T>
class stack
{
std::atomic<node<T>*> head;
public:
void push(const T& data)
{
node<T>* new_node = new node<T>(data);
// присваиваем текущее значение из head в new_node->next
new_node->next = head.load(std::memory_order_relaxed);
// теперь установим new_node новым head, но если head
// больше не является тем, что хранится в new_node->next
// (другой поток мог вставить свой new_node прямо сейчас)
// то присвоим новый head в new_node->next и повторим
while(!head.compare_exchange_weak(new_node->next, new_node,
std::memory_order_release,
std::memory_order_relaxed))
; // тело цикла пустое
// Примечание: код выше не является потокобезопасным по крайней мере в
// GCC до 4.8.3 (ошибка 60272), clang до 2014-05-05 (ошибка 18899)
// MSVC до 2014-03-17 (ошибка 819819). Ниже приводится обходной путь:
// node<T>* old_head = head.load(std::memory_order_relaxed);
// do {
// new_node->next = old_head;
// } while(!head.compare_exchange_weak(old_head, new_node,
// std::memory_order_release,
// std::memory_order_relaxed));
}
};
int main()
{
stack<int> s;
s.push(1);
s.push(2);
s.push(3);
}
Демонстрирует, как std::compare_exchange_strong изменяет значение атомарной переменной или переменной, используемой для сравнения.
| Этот раздел не завершён Причина: было бы неплохо показать больше примеров практического использования в системах компьютерной алгебры, таких, как в книге Параллелизм в Действии |
#include <atomic>
#include <iostream>
std::atomic<int> ai;
int tst_val= 4;
int new_val= 5;
bool exchanged= false;
void valsout()
{
std::cout << "ai= " << ai
<< " tst_val= " << tst_val
<< " new_val= " << new_val
<< " exchanged= " << std::boolalpha << exchanged
<< '\n';
}
int main()
{
ai= 3;
valsout();
// tst_val != ai ==> tst_val была изменена
exchanged = ai.compare_exchange_strong( tst_val, new_val );
valsout();
// tst_val == ai ==> ai была изменена
exchanged = ai.compare_exchange_strong( tst_val, new_val );
valsout();
}
Вывод:
ai= 3 tst_val= 4 new_val= 5 exchanged= false
ai= 3 tst_val= 3 new_val= 5 exchanged= false
ai= 5 tst_val= 3 new_val= 5 exchanged= true
Смотрите также
| атомарно сравнивает значение атомарного объекта с неатомарным аргументом и выполняет атомарный обмен, если они равны, или атомарную загрузку, если нет (шаблон функции) |