C++ C++  ·  30 вопросов  ·  ~85 мин  ·  ⏱ Таймер 25:00  ·  Сложный  · 

Атомарные операции и модель памяти

Профессиональный тест проверяет не названия memory_order, а способность доказать видимость данных между потоками. Задания охватывают read-modify-write, release/acquire, happens-before, ABA, ложное разделение кэша и ситуации, где атомарный счётчик не спасает составной инвариант.

Отвечено: 0 из 30
⏱ --:--
0%
💡 Инструкция: Выберите один ответ из пяти. В тесте 30 вопросов, время — 85 минут. Во многих заданиях недостаточно узнать конструкцию: проследите порядок вычислений, время жизни объектов, владение ресурсами и условия, при которых решение остаётся корректным. После завершения откроются правильные ответы, объяснения, общий результат и тематические шкалы.
Вопрос 1 из 30
Какое итоговое значение counter гарантировано после join обоих потоков?
C++C++20
#include <atomic>
#include <thread>
std::atomic<int> counter{0};
void work(){ for(int i=0;i<100000;++i) counter.fetch_add(1); }
int main(){ std::thread a(work), b(work); a.join(); b.join(); auto x=counter.load(); }
Вопрос 2 из 30
Почему `if (flag.load()) flag.store(false);` не является атомарным “забрать флаг”?
C++C++20
std::atomic<bool> flag{true};

void try_take() {
    if (flag.load(std::memory_order_relaxed))
        flag.store(false, std::memory_order_relaxed);
}
// Два потока одновременно вызывают try_take().
Вопрос 3 из 30
Что делает `compare_exchange_weak(expected, desired)` при неудаче сравнения?
C++C++20
std::atomic<int> state{7};
int expected = 5;
const bool changed = state.compare_exchange_weak(
    expected, 9,
    std::memory_order_acq_rel,
    std::memory_order_acquire);
// Чему равен expected, если сравнение не прошло?
Вопрос 4 из 30
Когда weak-версия CAS предпочтительна strong?
C++C++20
std::atomic<int> state{0};
int expected = 0;
while (!state.compare_exchange_weak(
    expected, 1,
    std::memory_order_acq_rel,
    std::memory_order_relaxed)) {
    expected = 0;
}
Вопрос 5 из 30
Какое требование у std::atomic_ref<T>?
C++C++20
alignas(std::atomic_ref<int>::required_alignment) int counter = 0;
std::atomic_ref<int> atomic_counter(counter);
atomic_counter.fetch_add(1, std::memory_order_relaxed);
Вопрос 6 из 30
Что гарантирует memory_order_relaxed?
C++C++20
std::atomic<unsigned> requests{0};
void record_request() {
    requests.fetch_add(1, std::memory_order_relaxed);
}
Вопрос 7 из 30
Какой store нужен издателю для классической публикации данных через ready?
C++C++20
data = build();               // обычные записи
ready.store(true, std::memory_order_release);
Вопрос 8 из 30
Какой load нужен читателю, чтобы после чтения true безопасно видеть опубликованные данные?
C++C++20
Data data;
std::atomic<bool> ready{false};

// поток-издатель
data = build_data();
ready.store(true, std::memory_order_release);

// поток-читатель
if (ready.load(/* какой порядок? */))
    consume(data);
Вопрос 9 из 30
Можно ли использовать memory_order_release для load?
C++C++20
std::atomic<int> value{0};
// какой порядок допустим для чистой загрузки?
int observed = value.load(std::memory_order_acquire);
Вопрос 10 из 30
Чем seq_cst сильнее acquire/release?
C++C++20
std::atomic<int> x{0};
std::atomic<int> y{0};
// Поток A
x.store(1, std::memory_order_seq_cst);
// Поток B
y.store(1, std::memory_order_seq_cst);
Вопрос 11 из 30
Когда возникает data race?
C++C++20
int value = 0;
// Поток A
value = 1;
// Поток B
std::cout << value; // нет mutex и atomic
Вопрос 12 из 30
Что гарантирует успешный lock mutex после unlock другого потока?
C++C++20
std::mutex mutex;
Data shared;
// Поток A
{
    std::lock_guard lock(mutex);
    shared.prepare();
}
// Поток B
{
    std::lock_guard lock(mutex);
    consume(shared);
}
Вопрос 13 из 30
Какое отношение создаёт `thread.join()`?
C++C++20
int result = 0;
std::thread worker([&] {
    result = calculate();
});
worker.join();
std::cout << result;
Вопрос 14 из 30
Безопасна ли публикация `ptr = new Data; ready.store(true, release)` и чтение ptr после acquire-ready?
C++C++20
Data* ptr = nullptr;
std::atomic<bool> ready{false};

// издатель
ptr = new Data{42};
ready.store(true, std::memory_order_release);

// читатель
if (ready.load(std::memory_order_acquire))
    std::cout << ptr->value;
Вопрос 15 из 30
Почему double-checked locking с обычным указателем без правильной атомарной публикации опасен?
C++C++20
Widget* instance = nullptr;
std::mutex init_mutex;
Widget* get() {
    if (instance == nullptr) {
        std::lock_guard lock(init_mutex);
        if (instance == nullptr)
            instance = new Widget;
    }
    return instance;
}
Вопрос 16 из 30
Какую гарантию продвижения даёт lock-free операция при продолжающейся конкуренции потоков?
Вопрос 17 из 30
Что такое проблема ABA в CAS-алгоритме?
Вопрос 18 из 30
Почему нельзя сразу delete удалённый узел lock-free стека?
Вопрос 19 из 30
Что делает hazard pointer?
Вопрос 20 из 30
Почему `atomic<T>::is_lock_free()` нужно воспринимать как свойство реализации и конкретного типа?
C++C++20
struct Pair { std::uint64_t lo, hi; };

std::atomic<int> small;
std::atomic<Pair> wide;

std::cout << small.is_lock_free() << ' '
          << wide.is_lock_free();
Вопрос 21 из 30
Обнаружит ли ThreadSanitizer логическую ошибку, где все доступы атомарны, но выбран слишком слабый memory_order?
Вопрос 22 из 30
Что такое litmus test модели памяти?
Вопрос 23 из 30
Почему ошибка acquire/release может не проявиться на x86, но проявиться на ARM?
Вопрос 24 из 30
Как повысить шанс проявления редкой ошибки публикации в тесте?
Вопрос 25 из 30
Как документировать CAS-цикл?
Вопрос 26 из 30
Что такое false sharing?
Вопрос 27 из 30
Как уменьшить false sharing двух горячих счётчиков?
Вопрос 28 из 30
Когда `atomic::wait/notify` лучше активного spin-loop?
C++C++20
std::atomic<int> state{0};

void wait_until_ready() {
    int old = state.load(std::memory_order_acquire);
    while (old == 0) {
        state.wait(old, std::memory_order_relaxed);
        old = state.load(std::memory_order_acquire);
    }
}
Вопрос 29 из 30
Почему два атомика `x` и `y` не обеспечивают инвариант `x + y == 100` для читателя?
C++C++20
std::atomic<int> x{50};
std::atomic<int> y{50};

// писатель переносит единицу из x в y
x.fetch_sub(1, std::memory_order_seq_cst);
y.fetch_add(1, std::memory_order_seq_cst);

// читатель
int snapshot = x.load() + y.load();
Вопрос 30 из 30
Когда обычный mutex предпочтительнее lock-free структуры?

Ответьте на все 30 вопросов, чтобы получить результат

🔗 Встроить тест на свой сайт (iframe) ▼

Скопируйте код и вставьте в любое место на вашем сайте:

Также доступна прямая ссылка на embed-страницу