Многопоточный ping-pong. Использовать memory_order_acquire / memory_order_release или memory_order_relaxed?

Имеется код пинг-понга (использую MSVC 2017).
Вопрос: является ли он корректным? или нужно использовать memory_order_acquire и memory_order_release?

При запуске он работает нормально.

#include <iostream>
#include <atomic>
#include <thread>

std::atomic<int> cur_thread_index = 0;
const int COUNT = 10;

void func(std::string text, int thread_index)
{
    for(int i = 0; i < COUNT; ++i)
    {
        while(cur_thread_index.load(std::memory_order_relaxed) != thread_index){}
        std::cout << text.c_str() << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(1));
        cur_thread_index.store(1 - thread_index, std::memory_order_relaxed);
    }
}

int main()
{
    std::thread t1(func, "ping", 0);
    std::thread t2(func, "pong", 1);

    t1.join();
    t2.join();

    return 0;
}

Ответы (1 шт):

Автор решения: Fat-Zer

Всё нормально. memory_order_relaxed говорит компилятору не добавлять барьер памяти при выполнении данной операции, а барьеры памяти имеет смысл добавлять только при работе более чем с одним разделяемым объектом: в таком случае результаты работы программы неотличимы от работы без барьера. Подробнее о том, что такое барьеры памяти смотри здесь.

Надо помнить, что x86 на уровне архитектуры не поддерживает ни какие расслабленные модели памяти и на нём в любом случае практически все* операции с атомарными переменными будут выполняться точно также как и операцииmemory_order_seq_cst. т.е. единственное, на что влияет этот параметр при компиляции под x86, на сколько свободно компилятор может переставлять вычисление операций.


* У меня нет полной уверенности, что это применимо абсолютно ко всем, хотя скорей всего это так

→ Ссылка