參考
1. 概述
智能指針包括一個實際數(shù)據(jù)指針和一個引用計數(shù)指針,這兩個操作不是一個指令可以完成的,因此多線程環(huán)境下,勢必有問題。

2. shared_ptr 的線程安全
2.1. shared_ptr 的線程安全結(jié)論
根據(jù)boost官方文檔 shared_ptr_thread_safety有如下結(jié)論:
- 同一個shared_ptr被多個線程讀,是線程安全的;
- 同一個shared_ptr被多個線程寫,不是線程安全的;
- 共享引用計數(shù)的不同的shared_ptr被多個線程寫,是線程安全的。
Examples:
shared_ptr<int> p(new int(42));
Code Example 4. Reading a shared_ptr from two threads,線程安全
// thread A
shared_ptr<int> p2(p); // reads p
// thread B
shared_ptr<int> p3(p); // OK, multiple reads are safe
Code Example 5. Writing different shared_ptr instances from two threads,線程安全
// thread A
p.reset(new int(1912)); // writes p
// thread B
p2.reset(); // OK, writes p2
Code Example 6. Reading and writing a shared_ptr from two threads,線程不安全
// thread A
p = p3; // reads p3, writes p
// thread B
p3.reset(); // writes p3; undefined, simultaneous read/write
Code Example 7. Reading and destroying a shared_ptr from two threads,線程不安全
// thread A
p3 = p2; // reads p2, writes p3
// thread B
// p2 goes out of scope: undefined, the destructor is considered a "write access"
Code Example 8. Writing a shared_ptr from two threads,線程不安全
// thread A
p3.reset(new int(1));
// thread B
p3.reset(new int(2)); // undefined, multiple writes
2.2. shared_ptr 多線程下可能出現(xiàn)的race condition
參考為什么多線程讀寫 shared_ptr 要加鎖?,假設(shè)一個shared_ptr的復(fù)制分兩個步驟:
- 復(fù)制ptr 指針
- 復(fù)制引用計數(shù)指針
考慮一個簡單的場景,有 3 個 shared_ptr<Foo> 對象 x、g、n:
shared_ptr<Foo> g(new Foo); // 線程之間共享的 shared_ptr
shared_ptr<Foo> x; // 線程 A 的局部變量
shared_ptr<Foo> n(new Foo); // 線程 B 的局部變量
一開始,各安其事。

線程 A 執(zhí)行
x = g;(即 read g),以下完成了步驟 1,還沒來及執(zhí)行步驟 2。這時切換到了 B 線程。
同時線程 B 執(zhí)行 g = n; (即 write G),兩個步驟一起完成了。


這時 Foo1 對象已經(jīng)銷毀,x.ptr 成了空懸指針!
最后回到線程 A,完成步驟 2:

多線程無保護(hù)地讀寫 g,造成了“x 是空懸指針”的后果。這正是多線程讀寫同一個 shared_ptr 必須加鎖的原因。
3. weak_ptr
weak_ptr最初的引入,是為了解決shared_ptr互相引用導(dǎo)致的內(nèi)存無法釋放的問題。weak_ptr不會增加引用計數(shù),不能直接操作對象的內(nèi)存(需要先調(diào)用lock接口),需要和shared_ptr配套使用。
同時,通過weak_ptr獲得的shared_ptr可以安全使用,因為其lock接口是原子性的,那么lock返回的是一個新的shared_ptr,不存在同一個shared_ptr的讀寫操作,除非后續(xù)又對這個新的shared_ptr又被其他線程同時讀寫。