Как выполнить двойную буферизацию с помощью атомных указателей?

Атомный новичок здесь. Мой код в настоящее время выглядит так (упрощенно):

std::atomic<Object*> object;

void thread_a()
{
  object.load()->doSomething(); // (1)
}

void thread_b()
{
    Object* oldObject = object.load();
    Object* newObject = new Object();
    // Update new object accordingly...

    while (!object.compare_exchange_weak(oldObject, newObject));

    delete oldObject;
}

Словом, моя идея состоит в том, чтобы позволить thread_b атомарно поменять общий объект (двойная буферизация), в то время как thread_a выполняет с ним некоторую работу. Мой вопрос: могу ли я предположить, что общий объект будет "защищен" от гонок данных, в то время как thread_a вызывает для него doSomething(), как это сделано в (1)?

Ответы

Ответ 1

Извлечение указателя с помощью load() будет атомарным, но сам вызов doSomething() не будет атомарным.

Это означает, что указатели можно поменять местами после doSomething() load() но перед doSomething() (что означает, что doSomething() вызывается с неправильным и теперь удаленным объектом).

Возможно, мьютекс может быть лучшим выбором здесь?

Ответ 2

Я делаю это довольно часто, но... с общими указателями и без блокировки!

Есть проблема с вашим дизайном, как предложил какой-то программист Чувак в своем ответе. Но если вы сделаете это с shared_ptr, и логика вашей программы это позволит, у вас все будет хорошо.

Причина, по которой это работает с shared_ptr заключается в том, что ваш объект не будет удален принудительно, пока он живет где-то еще. Итак, вот как вы это делаете:

std::shared_ptr<Object> object;

void thread_a()
{
  std::atomic_load(&object)->doSomething(); // (1)
}

void thread_b()
{
    std::shared_ptr<Object> oldObject = std::atomic_load(&object);
    std::shared_ptr<Object> newObject = std::make_shared<Object>();
    // Update new object accordingly...

    while (!std::atomic_compare_exchange_weak(object, oldObject, newObject));
}

РЕДАКТИРОВАТЬ: Этот atomic_load специализируется для shared_ptr. Это, кажется, вызвало путаницу в комментариях: https://en.cppreference.com/w/cpp/memory/shared_ptr/atomic