Что выведет код Middle Многопоточность и модель памяти обновлено

Что выведет: атомик, мьютекс и CAS

Вопрос

Что выведет программа (C++20, g++ 14)? Каждый вывод с новой строки, как в консоли.

#include <atomic>
#include <iostream>
#include <mutex>
#include <thread>
#include <vector>

int main() {
    std::atomic<int> counter{0};
    int plain = 0;
    std::mutex m;
    std::vector<std::thread> ts;
    for (int i = 0; i < 4; ++i)
        ts.emplace_back([&] {
            for (int j = 0; j < 1000; ++j) {
                counter.fetch_add(1, std::memory_order_relaxed);
                std::lock_guard lock(m);
                ++plain;
            }
        });
    for (auto& t : ts) t.join();
    std::cout << counter.load() << ' ' << plain << '\n';

    std::atomic<int> x{5};
    int expected = 5;
    bool ok = x.compare_exchange_strong(expected, 7);
    std::cout << ok << ' ' << x.load() << ' ' << expected << '\n';
    ok = x.compare_exchange_strong(expected, 9);
    std::cout << ok << ' ' << x.load() << ' ' << expected << '\n';
}

Попробуйте сами

Напишите, что выведет код: каждый вывод с новой строки.

Показать ответ и разбор

Коротко

Код выведет: 4000 4000, 1 7 5, 0 7 7. Атомарный счётчик (даже с relaxed) и счётчик под мьютексом дают точные 4000: инкременты не теряются, а join гарантирует, что все записи видны основному потоку.

Что выведет

4000 4000
1 7 5
0 7 7

Разбор

Атомарный счётчик (даже с relaxed) и счётчик под мьютексом дают точные 4000: инкременты не теряются, а join гарантирует, что все записи видны основному потоку. Первый CAS: x == expected == 5, записывается 7, expected не меняется. Второй CAS: x == 7 ≠ 5, запись не происходит, в expected попадает текущее значение 7.

Ответьте сами, а не прочитайте

В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.