先说结论,智能指针都是非线程安全的。
这里案例使用的是shared_ptr
,其他的unique_ptr
或者weak_ptr
的结果都是类似的,如下多线程调度代码:
#include
#include
#include
#include
#include
#include
#include #include using namespace std;class PTR{
public:PTR(){ }int GetData() { return data_;}void SetData(int a) { data_.store(a, std::memory_order_relaxed);}void DoSomething() { for (int i = 0; i< 10; i++) { data_.fetch_add(1, std::memory_order_relaxed);}}
private:std::atomic<int> data_;
};std::shared_ptr<PTR> ptr;
std::mutex mu;void ThreadFunc(int num) { if (!ptr) { ptr.reset(new PTR());ptr->SetData(2);}ptr->DoSomething();std::cout << "thread " << num << " GetData " << ptr->GetData() << " ref_count: " << ptr.use_count() << std::endl;ptr.reset();
}int main(int args, char* argv[]) { int threads = atoi(argv[1]);std::vector<std::thread> thread_vec;for (int i = 0; i < threads; i++) { thread_vec.emplace_back(std::thread(ThreadFunc, i));}for (auto& a : thread_vec) { a.detach();}return 0;
}
大体逻辑是多个线程访问 因为我们在 其他的智能指针通过reset构造对象的逻辑大体相似。 shared_ptr实现其实很简单,这里主要还是看一下它的reset逻辑,即 使用一个新的对象初始化当前shared_ptr 引用的对象 所以,这个过程本身就不是原子的,再加上外部同一个线程函数内部多次修改全局shared_ptr的地址,线程安全问题显而易见。 综上,大家在多线程内部使用共享的智能指针的时候需要减少对智能指针的修改,或者修改的时候加上锁同步,防止出现智能指针内部的不同步行为,对于ThreadFunc来说,修改以及访问的逻辑需要有锁的介入才行:shared_ptr
,每次执行之前如果发现这个ptr是空的,则会先初始化一下,再做一些累加逻辑,处理完成之后再设置为空。
因为ThreadFunc
中没有同步机制,我们多线程下可能的执行行为如下:
其中t1 < t2 < t3 ThreadFunc
内修改 一个被全局共享的ptr,所以,这个时候我们可能想要知道shared_ptr在被修改的时候内部行为是什么样子的。shared_ptr 的实现
_SafeConv<_Yp>
reset(_Yp* __p) // _Yp must be complete.
{ // Catch self-reset errors.__glibcxx_assert(__p == 0 || __p != _M_ptr);// 将__p 构造为shared_ptr,并且与当前shared_ptr进行地址以及引用计数的交换__shared_ptr(__p).swap(*this);
}// 直接交换两个对象的地址,再交换shared_ptr的引用计数
void
swap(__shared_ptr<_Tp, _Lp>& __other) noexcept
{ std::swap(_M_ptr, __other._M_ptr);_M_refcount._M_swap(__other._M_refcount);
}void
_M_swap(__shared_count& __r) noexcept
{ _Sp_counted_base<_Lp>* __tmp = __r._M_pi;__r._M_pi = _M_pi;_M_pi = __tmp;
}
而当我们通过operator->()
去访问shared_ptr 的时候则是没有任何问题的,多线程下只要不修改,任何的读都是ok的。element_type*
operator->() const noexcept
{
_GLIBCXX_DEBUG_PEDASSERT(_M_get() != nullptr);
return _M_get();
}element_type*
_M_get() const noexcept
{ return static_cast<const __shared_ptr<_Tp, _Lp>*>(this)->get(); }
};/// Return the stored pointer.
element_type*
get() const noexcept
{ return _M_ptr; }
void ThreadFunc(int num) { mu.lock();if (!ptr) { ptr.reset(new PTR());ptr->SetData(2);}ptr->DoSomething();std::cout << "thread " << num << " GetData " << ptr->GetData() << " ref_count: " << ptr.use_count() << std::endl;ptr.reset();mu.unlock();
}
C++11标准中可以为模板定义别名,比如
template
文章目录unique_ptr 智能指针的实现shared_ptr 智能指针的实现指针类型转换
unique_ptr 智能指针的实现
一个对象只能被单个unique_ptr 所拥有。
#include
文章目录weak_ptr描述声明作用原理实现函数成员使用总结
weak_ptr描述
声明
头文件:
文章目录shared_ptr描述声明作用原理实现函数使用关于shared_ptr循环引用问题
shared_ptr描述
声明
shared_ptr属于C++11特性中新加的一种智能指针,它的实现方式是模版类,头文件
enable_shared_from_this解析 enable_shared_from_this,是一个以其派生类为模板类型实参的基础模板,继承它,this指针就能变成shared_ptr。 什么时候该使用enable_shared_from_this模板类 在看下面的例子之前,简单说下使用背景,单有...
经过长期探索,发现一个不需要手动设置线程休眠时间(e.g. std::this_thread::sleep_for(std::chrono::microseconds(1)))的代码: Github: https://github.com/log4cplus/ThreadPool #ifndef THREAD_POOL_H_7e...
nth_element(first,nth,last) first,last 第一个和最后一个迭代器,也可以直接用数组的位置。 nth,要定位的第nn 个元素,能对它进行随机访问. 将第n_thn_th 元素放到它该放的位置上,左边元素都小于它,右边元素都大于它. 测试代码: http://www.cplusplus.com...
c/c++老版本的rand()存在一定的问题,在转换rand随机数的范围,类型或者分布时,常常会引入非随机性。
定义在
jsoncpp 是一个C++ 语言实现的json库,非常方便得支持C++得各种数据类型到json 以及 json到各种数据类型的转化。 一个json 类型的数据如下: {"code" : 10.01,"files" : "","msg" : "","uploadid" : "UP000000" } 这种数据类型方便我们人阅读以...
问题如下: 已知一组数(其中有重复元素),求这组数可以组成的所有子集中,子 集中的各个元素和为整数target的子集,结果中无重复的子集。 例如: nums[] = [10, 1, 2, 7, 6, 1, 5], target = 8 结果为: [[1, 7], [1, 2, 5], [2, 6], [1, 1, 6]] 同样之前有...