Свободный от блокировки обмен двух уникальных_ptr & lt; T & gt;

Обмен двух unique_ptrs не гарантированно является потокобезопасным.

std::unique_ptr<T> a, b;
std::swap(a, b); // not threadsafe

Так как мне нужны атомарные перестановки указателей и мне нравится обработка владения unique_ptrЕсть ли простой способ объединить их обоих?


Изменить: Если это невозможно, я открыт для альтернатив. Я по крайней мере хочу сделать что-то вроде этого:

threadshared_unique_ptr<T> global;

void f() {
threadlocal_unique_ptr<T> local(new T(...));
local.swap_content(global); // atomically for global
}

Какой идиоматический способ сделать это в C ++ 11?

35

Решение

Замена двух указателей без блокировки

Кажется, нет общего решения без блокировки для этой проблемы. Для этого вам нужна возможность атомарно записывать новые значения в две неконтинуальные области памяти. Это называется DCAS, но это не доступно в процессорах Intel.

Передача прав без блокировки

Это возможно, так как нужно только атомарно сохранить новое значение в global и получить его старое значение. Моей первой идеей было использовать CAS операция. Взгляните на следующий код, чтобы получить представление:

std::atomic<T*> global;

void f() {
T* local = new T;
T* temp = nullptr;
do {
temp = global;                                                   // 1
} while(!std::atomic_compare_exchange_weak(&global, &temp, local));  // 2

delete temp;
}

меры

  1. Запомнить текущий global указатель в temp
  2. Сохранить local в global если global все еще равен temp (это не было изменено другим потоком). Попробуйте еще раз, если это не так.

На самом деле, CAS там перебор, так как со старым мы ничего особенного не делаем global значение до его изменения. Итак, мы просто можем использовать операцию атомного обмена:

std::atomic<T*> global;

void f() {
T* local = new T;
T* temp = std::atomic_exchange(&global, local);
delete temp;
}

Увидеть Джонатана ответ для еще более короткого и элегантного решения.

В любом случае вам придется написать свой умный указатель. Вы не можете использовать этот трюк со стандартным unique_ptr,

17

Другие решения

Идиоматический способ атомарного изменения двух переменных — использовать блокировку.

Вы не можете сделать это для std::unique_ptr без замка. Четное std::atomic<int> не предоставляет способ поменять местами два значения атомарно. Вы можете обновить один элементарно и вернуть его предыдущее значение, но подкачка концептуально состоит из трех этапов, с точки зрения std::atomic API они являются:

auto tmp = a.load();
tmp = b.exchange(tmp);
a.store(tmp);

Это атомная читать с последующим атомным чтение-модификация-запись с последующим атомным записывать. Каждый шаг может быть сделан атомарно, но вы не можете сделать все три атомарно без блокировки.

Для не копируемого значения, такого как std::unique_ptr<T> вы даже не можете использовать load а также store Операции выше, но должны сделать:

auto tmp = a.exchange(nullptr);
tmp = b.exchange(tmp);
a.exchange(tmp);

Это три чтение-модификация-запись операции. (Вы не можете использовать std::atomic<std::unique_ptr<T>> чтобы сделать это, потому что это требует тривиально копируемого типа аргумента, и std::unique_ptr<T> не копируемый.)

Чтобы сделать это с меньшим количеством операций, потребуется другой API, который не поддерживается std::atomic потому что это не может быть реализовано, потому что, как говорит ответ Стаса, это невозможно с большинством процессоров. Стандарт C ++ не имеет привычки стандартизировать функциональность, которая невозможна на всех современных архитектурах. (В любом случае не намеренно!)

Изменить: Ваш обновленный вопрос спрашивает об очень другой проблеме, во втором примере вам не нужен атомный своп, который влияет на два объекта. Только global распределяется между потоками, поэтому вам все равно, будут ли обновляться local атомарный, вам просто нужно атомарно обновить global и получить старое значение. Канонический C ++ 11 способ сделать это с std:atomic<T*> и вам даже не нужна вторая переменная:

atomic<T*> global;

void f() {
delete global.exchange(new T(...));
}

Это один чтение-модификация-запись операция.

17

По вопросам рекламы [email protected]