shared_ptr应用细节
shared_ptr应用细节
boost与C++11中shared_ptr对数组支持的区别
boost中的shared_ptr支持数组而C++ 11中的shared_ptr还不支持,要在C++17中才有对数组支持。boost 从1.53版本起添加了对数组的支持。如下代码的测试环境是VS2015 x64
#include "boost/smart_ptr.hpp"
//#include <memory>
#define _crtdbg_map_alloc
int main()
{
{
boost::shared_ptr<int[1024]> p(new int[1024]);
//std::shared_ptr<int[1024]> p(new int[1024]);
}
_CrtDumpMemoryLeaks();
}
shared_ptr< void > 万能指针
不管是std中的还是boost中的,shared_ptr< void >可以用于任何类型的动态对象,在析构时都可以正确delete 该类型对象。参考如下资料
Why do std::shared_ptr< void > work
boost 1.66 shared_ptr文档
shared_ptr线程安全
boost 中的和STL中的对线程安全性要求不一样
- boost shared_ptr的线程安全
一个shared_ptr实体可以被多个线程同时读。不同shared_ptr实体可以多个线程同时写。如果要从多个线程读写同一个shared_ptr对象,那么需要加锁。
- STL shared_ptr的线程安全
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 shared_ptr without synchronization and any of those accesses uses a non-const member function of shared_ptr then a data race will occur;
照意思理解,对同一个对象分享所有权的shared_ptr在多个线程上是可以进行读写操作而不需要加锁。官网上也一个例子说明这种用法。(这里有点不太懂,不知道怎么去验证)
- 在vs2015 x64跑如下代码
#include <iostream>
#include <memory>
#include <thread>
#include <chrono>
#include <mutex>
struct Base
{
Base() { std::cout << " Base::Base()\n"; }
// Note: non-virtual destructor is OK here
~Base() { std::cout << " Base::~Base()\n"; }
};
struct Derived: public Base
{
Derived() { std::cout << " Derived::Derived()\n"; }
~Derived() { std::cout << " Derived::~Derived()\n"; }
};
void thr(std::shared_ptr<Base> p)
{
std::shared_ptr<Base> lp = p; // thread-safe, even though the
// shared use_count is incremented
{
static std::mutex io_mutex;
std::lock_guard<std::mutex> lk(io_mutex);
std::cout << "local pointer in a thread:\n"
<< " lp.get() = " << lp.get()
<< ", lp.use_count() = " << lp.use_count() << '\n';
}
}
int main()
{
std::shared_ptr<Base> p = std::make_shared<Derived>();
std::cout << "Created a shared Derived (as a pointer to Base)\n"
<< " p.get() = " << p.get()
<< ", p.use_count() = " << p.use_count() << '\n';
std::thread t1(thr, p), t2(thr, p), t3(thr, p);;
t1.join(); t2.join(); t3.join();
std::cout << "All threads completed, the last one deleted Derived\n";
}
代码中英文注释的那段,每个线程的lp变量是共享p变量的,在STL中这种操作是线程安全的。
这里关注下运行结果,在VS2015 x64下跑这段代码,运行结果如下:
Base::Base()
Derived::Derived()
Created a shared Derived (as a pointer to Base)
p.get() = 0000020D93A291B0, p.use_count() = 1
local pointer in a thread:
lp.get() = 0000020D93A291B0, lp.use_count() = 3
local pointer in a thread:
lp.get() = 0000020D93A291B0, lp.use_count() = 7
local pointer in a thread:
lp.get() = 0000020D93A291B0, lp.use_count() = 3
All threads completed, the last one deleted Derived
Derived::~Derived()
Base::~Base()
可以看到这里us_count跑出来的最大是7(每次跑出的结果都不一样),我预想的最大值应该是4,计算方式我推测应该是如下:一个 main thread + thread 中的三个复制拷贝 + 三个线程代码中 std::shared_ptr lp = p 语句的复制拷贝 = 7(是这样吗?)
借助shared_ptr来实现多线程中的”写时复制”
写时复制的概念:只有在真正需要资源时才去分配资源。”写时复制”应用在多线程同步的场景可以描述如下:
在系统中,有多个读线程和写线程共享一个数据结构。如果系统中的读操作的概率远远大于写操作,因为读写互斥的需要也会造成读线程间也需要加锁互斥,这无疑降低了系统处理的速度。其实只有当写线程进行操作时,才需要读写线程间的加锁互斥而读线程间是无需加锁的。所以运用写时复制的概念,在写线程判断如果有读线程在操作则拷贝一份数据进行更新。shared_ptr的引用计数的特性可以判断是否有读线程在进行数据读取,来实现写时复制。
示例数据结构如下:
typedef int data;
typedef vecotr<data> datalist;
typedef shared_ptr<datalist> datalistPtr;
datalistPtr g_datas;
读线程代码:
void read()
{
datalistPtr localDatas;
{
lock l(mutex);
//会增加引用计数
localDatas = g_datas;
}
for (int i=0; i< localDatas->size(); ++i)
{
//读操作
}
}
读线程中的加锁是针对shared_ptr的,在操作数据时是不用加锁的。
写线程代码:
void write()
{
lock l(mutex);
if (!g_datas.unique())
{//判断是否有读线程操作数据
datalistPtr newData(new datalist(*g_datas));
g_datas.swap(newData);
}
//对g_datas进行写操作
}
通过unique()判断当前的引用计数是否为1(即没有读线程对g_datas进行操作),如果不为1则复制一份旧数据(交换shared_ptr的指向)进行更新操作。在write函数中的锁必须是全程都锁住的,在有读线程操作数据时则实现对数据的互斥访问(g_datas.unique()为false),在没有读线程操作数据(g_datas.unique()为true)时对shared_ptr的读操作进行互斥。
以上。
资料:
boost shared_ptr官方文档
http://www.boost.org/doc/libs/1_66_0/libs/smart_ptr/doc/html/smart_ptr.html#shared_ptr
shared_ptr< void >作为万能指针的原理
https://*.com/questions/5913396/why-do-stdshared-ptrvoid-work#
C++11 shared_ptr官方文档
http://en.cppreference.com/w/cpp/memory/shared_ptr
vs2015 stl多线程安全的描述,里面有对shared_ptr线程安全的描述与C++11标准描述一致
https://msdn.microsoft.com/en-us/library/c9ceah3b(v=vs.140).aspx