1.安全性
在多线程环境下对同一个shared_ptr对象读操作没有问题,它的引用计数是原子的,安全且无锁,但是如果是多线程下有读写操作,以及对shared_ptr指向的对象有读写操作,那么就会发生竞争。shared_ptr多线程问题的本质是它所指向的对象的引用计数是否会因为多线程环境而出错,后一种情况就相当于普通指针,或认为是int写操作。
2.讨论
2.1 多线程独写所指向的对象
转自:https://www.cnblogs.com/gqtcgq/p/7492772.html
shared_ptr<long> global_instance = make_shared<long>(0); std::mutex g_i_mutex; void thread_fcn() { //std::lock_guard<std::mutex> lock(g_i_mutex);//加锁之后结果正确 //shared_ptr<long> local = global_instance; for(int i = 0; i < 100000000; i++) { *global_instance = *global_instance + 1; //*local = *local + 1;//就算使用这个也会出错,因为指向的是同一个global_ins } } int main(int argc, char** argv) { thread thread1(thread_fcn); thread thread2(thread_fcn); thread1.join(); thread2.join(); cout << "*global_instance is " << *global_instance << endl; return 0; }
这就是常见的非线性安全的场景。
2.2 多线程独写shared_ptr对象
转自:http://blog.csdn.net/solstice/article/details/8547547
虽然shared_ptr的引用计数是原子安全的,但是它有两个数据成员,指针和引用计数,对它们一同的写不能原子化。这里其实还是回到了它的本质,所指向对象的引用计数是否会因为多线程环境出现安全问题。
2.1 shared_ptr的安全级别
• 一个 shared_ptr 对象实体可被多个线程同时读取;
// thread A shared_ptr<int> p2(p); // reads p // thread B shared_ptr<int> p3(p); // OK, multiple reads are safe
• 两个 shared_ptr 对象实体可以被两个线程同时写入,“析构”算写操作;
// thread A p.reset(new int(1912)); // writes p // thread B p2.reset(); // OK, writes p2
• 如果要从多个线程读写同一个 shared_ptr 对象,那么需要加锁。
// thread A p = p3; // reads p3, writes p 读p3 // thread B p3.reset(); // writes p3; undefined, simultaneous read/write 写p3
// thread A p3.reset(new int(1));//写p3 // thread B p3.reset(new int(2)); // undefined, multiple writes 写p3
上面链接中给出了较为详细的图解来说明,主要问题还是指针指向更新和引用计数更新不同步的问题,导致空悬指针及引用计数出错的问题。
3.shared_ptr两种初始化的不同
ref_count也需要一块堆空间,(这里vptr是什么?虚拟函数表的指针?为什么会放到这里?)
shared_ptr<Foo> x(new Foo);
这种方式需要为 Foo 和 ref_count 各分配一次内存,而使用
shared_ptr<Foo> x=make_shared<Foo>();
会一次分配较大的内存供 Foo 和 ref_count 对象容身,(存放到一块地址空间)。【这里又提到参数是通过完美转发传递给Foo的构造函数的】