Решение, предложенное RobH, не работает. У него та же проблема, что и у исходного вопроса: при доступе к объекту подсчета ссылок он, возможно, уже был удален.
Единственный способ решить проблему без глобальной блокировки (как в boost :: atomic_store) или инструкций условного чтения / записи - это каким-то образом отложить уничтожение объекта (или объекта общего подсчета ссылок, если такая вещь есть). используемый). Так что у Зеннехой есть хорошая идея, но его метод слишком небезопасен.
Я мог бы сделать это, сохранив копии всех указателей в потоке писателя, чтобы писатель мог контролировать уничтожение объектов:
class Writer : public Thread {
virtual void Run() {
list<SmartPtr> ptrs; //list that holds all the old ptr values
for (;;) {
SmartPtr newPtr(new Object);
if(ptr)
ptrs.push_back(ptr); //push previous pointer into the list
ptr = newPtr;
//Periodically go through the list and destroy objects that are not
//referenced by other threads
for(auto it=ptrs.begin(); it!=ptrs.end(); )
if(it->refCount()==1)
it = ptrs.erase(it);
else
++it;
}
}
};
Однако все еще существуют требования к классу интеллектуальных указателей. Это не работает с shared_ptr, так как чтение и запись не являются атомарными. Это почти работает с boost :: intrusive_ptr. Назначение для intrusive_ptr реализовано так (псевдокод):
//create temporary from rhs
tmp.ptr = rhs.ptr;
if(tmp.ptr)
intrusive_ptr_add_ref(tmp.ptr);
//swap(tmp,lhs)
T* x = lhs.ptr;
lhs.ptr = tmp.ptr;
tmp.ptr = x;
//destroy temporary
if(tmp.ptr)
intrusive_ptr_release(tmp.ptr);
Насколько я понимаю, единственное, чего здесь не хватает, это ограничения памяти уровня компилятора до lhs.ptr = tmp.ptr;
. При этом добавляются, что чтение rhs
и запись lhs
будут поточно-ориентированными при строгих условиях: 1) архитектура x86 или x64 2) атомарный подсчет ссылок 3) rhs
refcount не должен обнуляться во время назначения (гарантировано по описанному выше коду Writer) 4) только один поток записывает в lhs
(используя CAS вы можете иметь несколько писателей).
В любом случае, вы можете создать свой собственный класс интеллектуальных указателей на основе intrusive_ptr с необходимыми изменениями. Определенно проще, чем повторная реализация shared_ptr. И, кроме того, если вам нужна производительность, навязчивый путь.