如何修改shared_ptr智能指針,讓他支持多線程?
反對匿名用戶的答案,(boost或C++11的)shared_ptr不是線程安全的,讀安全,寫不安全
最簡單的方法就是給shared_ptr用鎖保護,因為如果想要修改shared_ptr內部的實現來支持多線程,寫操作時會涉及到多個地址的更改,用簡單的單地址的CAS也是做不到的。所以,不想使用鎖的話,最好對shared_ptr只讀不寫
===========================
放嘲諷:下面答案和評論的知友們,你們真的明白什麼是線程安全和線程不安全嘛 =。=
https://isocpp.org/files/papers/N4162.pdf
Shared_ptr atomic access, revision 1
更新一下,std::atomic&
shared_ptr是值語義,多個線程對shared_ptr賦值和對一個結構體賦值一樣的。單就shared_ptr來說只要保證賦值時不衝突即可。另外shared_ptr指向的東西是否線程安全另說。
傳送門:http://www.cppblog.com/Solstice/archive/2013/01/28/197597.html
線程安全個人理解:
存在同一存儲空間的讀,是線程安全的。同一存儲空間只要涉及到了寫(邊讀邊寫,邊寫邊寫),就不安全了。
有個想法是寫一個wrapped_shared_ptr template.把寫操作加上鎖和讀操作區分開來。
我們實現了完全線程安全的 shared_ptr
例如:
// shared_ptr&
// 若發現 shared_ptr_in 是最後一個指向某地址塊的共享指針,就需要解決此時的多線程操作的安全性問題
// 解決辦法:
// 實質上 實現了一個 樂觀鎖來檢測衝突, 發現衝突後自動重新來過, 並且正在釋放的 shared_ptr也需要具備檢測到衝突後延遲 X 秒之後再釋放. 否則, 可能其他線程操作到已釋放的內存。我們實現了類似java的延遲垃圾回收機制。
我們重寫了整個shared_ptr庫的代碼,並且糾正了至少一處std::shared_ptr庫底層代碼庫的BUG。
我們壓縮了shared_ptr的內部兩個計數器shared_count/werk_cout,分別放到一個64bit整數的32bit低位和高位32bit, 更進一步解決了釋放衝突的問題,性能上也比std庫的實現快。
用硬體的DCAS指令可以使std::shared_ptr實現無鎖線程安全。最新Linux和Windows都是支持DCAS硬體指令的,所以最新C++編譯器中,std::atomic_is_lock_free&
這種問題應該去stackoverflow去問的。
iso主席herb sutter在他的"effective concurrency"中的回答:GotW #95 Solution: Thread Safety and Synchronization
自己擼一個線程安全的
雖然借用shared_ptr來實現線程安全的對象釋放,但是shared_ptr本身不是100%線程安全的。它的引用記數本身是安全且無鎖的,但對象的讀寫則不是,因為shared_ptr有兩個數據成員,讀寫操作不能原子化。shared_ptr的線程安全級別和內建類型、標準庫容器、std::string一樣。
一個shared_ptr對象實體可被多個線程同時讀取。
兩個shared_ptr對象實體可以被兩個線程同時寫入,「析構」算寫操作。
如果要從多個線程讀寫同一個shared_ptr對象,那麼需要加鎖。
上面是shared_ptr對象本身的線程安全級別,不是它管理的對象的安全級別。
要在多個線程中同時訪問同一個shared_ptr,正確的做法是用mutex保護。
local copy存在,shared_ptr作為函數參數傳遞時不必複製。shared_ptr本來就是線程安全的,除非你自己傳的是引用
-----------------------------
有人說shared_ptr不是線程安全的,補充一下,這是vs2013的部分實現代碼。
另外我說的除非你自己傳的是引用意思是說,除非你把一個線程里的shared_ptr當引用傳到另一個線程里去了,這樣肯定不是線程安全的。
代碼里_InterlockedCompareExchange明顯是原子操作。若不是為了線程安全,何必多此一舉。
bool _Incref_nz()
{ // increment use count if not zero, return true if successful
for (; ; )
{ // loop until state is known
#if defined(_M_IX86) || defined(_M_X64) || defined(_M_CEE_PURE)
_Atomic_integral_t _Count =
static_cast&
if (_Count == 0)
return (false);
if (static_cast&<_Atomic_integral_t&>(_InterlockedCompareExchange(
reinterpret_cast&
_Count + 1, _Count)) == _Count)
return (true);
#else /* defined(_M_IX86) || defined(_M_X64) || defined(_M_CEE_PURE) */
_Atomic_integral_t _Count =
_Load_atomic_counter(_Uses);
if (_Count == 0)
return (false);
if (_Compare_increment_atomic_counter(_Uses, _Count))
return (true);
#endif /* defined(_M_IX86) || defined(_M_X64) || defined(_M_CEE_PURE) */
}
}
Ptr 就算做到線程安全了,指向的對象不是線程安全一樣玩兒完。C++ RAII 式包裝在單線程里玩玩就好,擴展到多線程純屬自找混亂。
用一個綁定線程到auto_ptr 最好,確保任何時間只有一個線程可以訪問對象。
推薦閱讀: