c++ - std::enable_shared_from_this:是否允许在析构函数中调用 shared_from_this()? - IT工具网
项目因为业务升级,从单实例的大量异步,变为N实例的大量异步,用单一裸指针去管理已不合适。
于是不得不用到std::shared_ptr、std::enable_shared_from_this,刚开始没问题,某一天,突然崩了,debug下给出的也是一些无法看出问题的报错(能力有限)。
为解决bug,查阅了大量关于shared_ptr线程安全的资料,得知:
https://en.cppreference.com/w/cpp/memory/shared_ptr#Implementation_notes
依据:CPP官网All member functions (including copy constructor and copy assignment) can be called by multiple threads on different instances of shared_ptr without additional synchronization even if these instances are copies and share ownership of the same object. If multiple threads of execution access the same instance of shared_ptr without synchronization and any of those accesses uses a non-const member function of shared_ptr then a data race will occur; the shared_ptr overloads of atomic functions can be used to prevent the data race.
词霸机翻:
所有成员函数(包括拷贝构造函数和拷贝分配)都可以在不同的shared_ptr实例上被多个线程调用,而不需要额外的同步,即使这些实例是副本并共享同一对象的所有权。如果多个执行线程在没有同步的情况下访问相同的shared_ptr实例,并且其中任何一个访问使用了shared_ptr的非常量成员函数,那么将会发生数据竞争;原子函数的shared_ptr过载可以用来防止数据竞争。
类shared_ptr下,所有非const函数,都不是线程安全的。
并提到,如果要安全,那就用原子函数atomic_xxxx(C++11起可用,C++20中移除并用atomic类来实现)
看了std::shared_ptr的类实现,只有以下这些是const成员函数(线程安全)
constexpr shared_ptr() noexcept = default;
constexpr shared_ptr(nullptr_t) noexcept {} // construct empty shared_ptr
template <class _Ty2 = _Ty, enable_if_t<!disjunction_v<is_array<_Ty2>, is_void<_Ty2>>, int> = 0>
_NODISCARD _Ty2& operator*() const noexcept {
return *get();
}
template <class _Ty2 = _Ty, enable_if_t<!is_array_v<_Ty2>, int> = 0>
_NODISCARD _Ty2* operator->() const noexcept {
return get();
}
template <class _Ty2 = _Ty, class _Elem = element_type, enable_if_t<is_array_v<_Ty2>, int> = 0>
_NODISCARD _Elem& operator[](ptrdiff_t _Idx) const noexcept /* strengthened */ {
return get()[_Idx];
}
#if _HAS_DEPRECATED_SHARED_PTR_UNIQUE
_CXX17_DEPRECATE_SHARED_PTR_UNIQUE _NODISCARD bool unique() const noexcept {
// return true if no other shared_ptr object owns this resource
return this->use_count() == 1;
}
#endif // _HAS_DEPRECATED_SHARED_PTR_UNIQUE
explicit operator bool() const noexcept {
return get() != nullptr;
}
于是,开始搞atomic
#include <atomic>
void fun_read()
{
std::shared_ptr<myclass> sp_old;
std::shared_ptr<myclass> sp_new;
//读取
std::shared_ptr<myclass> sp_new = std::atomic_load(&sp_old);
}
void fun_write()
{
std::shared_ptr<myclass> sp_old;
std::shared_ptr<myclass> sp_new;
//写入
std::atomic_store(&sp_old, sp_new);
}
我的场景是,要把shared_ptr作为函数入参,开异步线程。
std::shared_ptr<A> sp;//各线程共用的
//原先,这么写
boost::scoped_thread<boost::detach> ot(myclass::myfun,
sp,
shared_from_this()
);
//现在,这么写
boost::scoped_thread<boost::detach> ot(myclass::myfun,
std::atomic_load(&sp),
shared_from_this()
);