метод std:: atomic load уменьшает количество ссылок при использовании с std:: shared ptr
Я хотел бы использовать std::atomic<std::shared_ptr>
в моем коде, чтобы shared_ptr можно было атомарно обновить, но у меня есть проблема при доступе к shared_ptr. Метод load () на atomic, похоже, уменьшает ref-count на shared_ptr, так что я не могу фактически использовать объект без его освобождения.
вот упрощенный фрагмент кода, который показывает проблему...
typedef shared_ptr<MyClass> MyClassPtr;
typedef atomic<MyClassPtr> MyClassAtomicPtr;
// 1.
MyClassPtr ptr( new MyClass() );
printf("1. use_count=%dn", ptr.use_count());
// 2.
MyClassAtomicPtr atomicPointer(ptr);
printf("2. use_count=%dn", ptr.use_count());
// 3.
{
MyClassPtr p = atomicPointer.load();
printf("3a. use_count=%dn", ptr.use_count());
}
printf("3b. use_count=%dn", ptr.use_count());
// 4.
{
MyClassPtr p = atomicPointer.load();
printf("4a. use_count=%dn", ptr.use_count());
}
printf("4b. use_count=%dn", ptr.use_count());
вывод этого:
1. use_count=1
2. use_count=2
3a. use_count=2
3b. use_count=1
4a. use_count=1
4b. use_count=-572662307
Я понимаю шаги 1 и 2. Но на Шаг 3, я ожидал бы, что назначение shared_ptr увеличит ref-count до 3, а затем, когда он выйдет из области для ref-count, вернется к 2. Но на самом деле он остается на 2 при назначении, а затем уменьшается до 1, когда shared_ptr выходит за рамки. Аналогично на шаге 4, где ref-count идет к нулю, и объект удаляется.
Итак, мой вопрос: как я могу получить доступ и использовать shared_ptr, управляемый atomic, не уничтожая его?
(Я был компиляция с Visual Studio 2012 версии 11.0.50727.1 RTMREL)
3 ответов
Я считаю, что стандартный способ атомарной загрузки и хранения общих указателей-использовать функции в §20.7.2.5[util.класса smartptr.общий.атомный.] Кажется, только libc++ clang поддерживает их:
template<class T> bool atomic_is_lock_free(const shared_ptr<T>* p);
template<class T> shared_ptr<T> atomic_load(const shared_ptr<T>* p);
template<class T> shared_ptr<T> atomic_load_explicit(const shared_ptr<T>* p, memory_order mo);
template<class T> void atomic_store(shared_ptr<T>* p, shared_ptr<T> r);
template<class T> void atomic_store_explicit(shared_ptr<T>* p, shared_ptr<T> r, memory_order mo);
template<class T> shared_ptr<T> atomic_exchange(shared_ptr<T>* p, shared_ptr<T> r);
template<class T> shared_ptr<T> atomic_exchange_explicit(shared_ptr<T>* p, shared_ptr<T> r, memory_order mo);
template<class T> bool atomic_compare_exchange_weak(shared_ptr<T>* p, shared_ptr<T>* v, shared_ptr<T> w);
template<class T> bool atomic_compare_exchange_strong(shared_ptr<T>* p, shared_ptr<T>* v, shared_ptr<T> w);
template<class T> bool atomic_compare_exchange_weak_explicit(shared_ptr<T>* p, shared_ptr<T>* v, shared_ptr<T> w, memory_order success, memory_order failure);
template<class T> bool atomic_compare_exchange_strong_explicit(shared_ptr<T>* p, shared_ptr<T>* v, shared_ptr<T> w, memory_order success, memory_order failure);
таким образом, вы можете написать код как:
auto ptr = std::make_shared<MyClass>();
printf("1. use_count=%d\n", ptr.use_count());
{
auto p = std::atomic_load(&ptr);
printf("3a. use_count=%d\n", ptr.use_count());
}
printf("3b. use_count=%d\n", ptr.use_count());
{
auto p = std::atomic_load(&ptr);
printf("3a. use_count=%d\n", ptr.use_count());
}
printf("4b. use_count=%d\n", ptr.use_count());
но я не могу найти такие поддержки, перечисленные в MSDN, поэтому лучшее, что вы можете сделать, это использовать мьютекс. (На самом деле, реализация этих функций в libc++ также использует мьютекс.)
вы не можете использовать std::shared_ptr<T>
как тип аргумента шаблона std::atomic<T>
. "Тип типового аргумента T должен быть тривиально копируемым."(§29.5 1 в N3290) std::shared_ptr<T>
не тривиально копируемым.
видимо, в вашем примере std::memcpy
(или что-то в этом роде) используется для копирования std::shared_ptr
и после этого вызывается деструктор. В этом причина уменьшения количества ссылок. На последнем шаге, объект удаляется.
решение заключается в использовании std::mutex
для защитите свой std::shared_ptr
.
внизу в кишках реализации std:: atomic ctor, который вы вызываете, должен назначать свой внутренний указатель чем-то вроде:
std::atomic(T* ctorInput) {
memcpy(myPtr, ctorInput, sizeof(T));
}
это означает, что он делает прямую копию на байтах, минуя любой реальный конструктор копирования "T(const T&)". Вот почему он работает только правильно на "тривиально копируемом" типе, а именно тот, чей конструктор копирования ничего не делает. Поскольку shared_ptr делает реальную работу, а именно атомарный приращение, в его копии ctor эта работа не выполняется std:: atomic, потому что она никогда не вызывает вызов. Вы тогда получите таинственный на 1 ошибку в счетчик.