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的構造函數的】