exponenta event banner

Атомная нагрузка и последовательность хранения не атомарная

Переменная, доступная между операциями загрузки и хранения

Описание

Этот дефект возникает при использовании этих функций для загрузки, а затем сохранения атомной переменной.

  • Функции C:

    • atomic_load()

    • atomic_load_explicit()

    • atomic_store()

    • atomic_store_explicit()

  • Функции C++:

    • std::atomic_load()

    • std::atomic_load_explicit()

    • std::atomic_store()

    • std::atomic_store_explicit()

    • std::atomic::load()

    • std::atomic::store()

Поток не может прервать атомарную нагрузку или операцию сохранения атома для переменной, но поток может прервать сохранение и затем загрузить последовательность.

Риск

Поток может изменять переменную между операциями загрузки и хранения, что приводит к условию гонки данных.

Зафиксировать

Чтобы считывать, изменять и сохранять переменную атомарно, используйте оператор составного назначения, например +=, atomic_compare_exchange() или atomic_fetch_*- функции семьи.

Примеры

развернуть все


#include <stdatomic.h>
#include <stdbool.h>

static atomic_bool flag = ATOMIC_VAR_INIT(false);

void init_flag(void)
{
    atomic_init(&flag, false);
}

void toggle_flag(void)
{
    bool temp_flag = atomic_load(&flag);
    temp_flag = !temp_flag;
    atomic_store(&flag, temp_flag);
}

bool get_flag(void)
{
    return atomic_load(&flag);
}

В этом примере переменная flag типа atomic_bool ссылается дважды внутри toggle_flag() функция. Функция загружает переменную, отменяет ее значение, а затем сохраняет новое значение обратно в переменную. Если вызывается два потока toggle_flag(), второй поток может получить доступ flag между операциями загрузки и хранения первого потока. flag может оказаться в неправильном состоянии.

Исправление - изменение переменной с помощью составного назначения

Одной из возможных корректировок является использование оператора составного назначения для переключения значения flag. Стандарт C определяет операцию с помощью ^= как атомарный.


#include <stdatomic.h>
#include <stdbool.h>

static atomic_bool flag = ATOMIC_VAR_INIT(false);

void toggle_flag(void)
{
    flag ^= 1;
}

bool get_flag(void)
{
    return flag;
}

Информация о результатах

Группа: Параллелизм
Язык: C | C++
По умолчанию: Вкл для рукописного кода, выкл для сгенерированного кода
Синтаксис командной строки: ATOMIC_VAR_SEQUENCE_NOT_ATOMIC
Воздействие: среднее
Представлен в R2018b