我正在閱讀 libstdc 的 std::shared_ptr 的實作,我注意到 libstdc 具有三個鎖定策略:_S_single、_S_mutex 和 _S_atomic(參見此處),并且鎖定策略會影響類 _Sp_counted_base(_M_add_ref和_M_release)的特化)
下面是代碼片段:
_M_release_last_use() noexcept
{
_GLIBCXX_SYNCHRONIZATION_HAPPENS_AFTER(&_M_use_count);
_M_dispose();
// There must be a memory barrier between dispose() and destroy()
// to ensure that the effects of dispose() are observed in the
// thread that runs destroy().
// See http://gcc.gnu.org/ml/libstdc /2005-11/msg00136.html
if (_Mutex_base<_Lp>::_S_need_barriers)
{
__atomic_thread_fence (__ATOMIC_ACQ_REL);
}
// Be race-detector-friendly. For more info see bits/c config.
_GLIBCXX_SYNCHRONIZATION_HAPPENS_BEFORE(&_M_weak_count);
if (__gnu_cxx::__exchange_and_add_dispatch(&_M_weak_count,
-1) == 1)
{
_GLIBCXX_SYNCHRONIZATION_HAPPENS_AFTER(&_M_weak_count);
_M_destroy();
}
}
template<>
inline bool
_Sp_counted_base<_S_mutex>::
_M_add_ref_lock_nothrow() noexcept
{
__gnu_cxx::__scoped_lock sentry(*this);
if (__gnu_cxx::__exchange_and_add_dispatch(&_M_use_count, 1) == 0)
{
_M_use_count = 0;
return false;
}
return true;
}
我的問題是:
- 使用 _S_mutex 鎖定策略時, __exchange_and_add_dispatch 函式可能只保證原子性,但可能無法保證它是完全隔離的,對嗎?
- 并且因為 1,'__atomic_thread_fence (__ATOMIC_ACQ_REL)' 的目的是確保當執行緒 A 呼叫 _M_dispose 時,沒有執行緒會呼叫 _M_destory(因此執行緒 A 永遠無法訪問函式 '_M_dispose 中的銷毀成員(例如:_M_ptr) '?
- 最讓我困惑的是,如果 1 和 2 都正確,那為什么在呼叫 '_M_dispose' 之前不需要添加執行緒柵欄?(因為_Sp_counted_base和_Sp_counted_base管理的物件本身在參考計數降為零時也有同樣的問題)
uj5u.com熱心網友回復:
其中一些在檔案和您參考的代碼中顯示的 URL 中得到了回答。
- 使用 _S_mutex 鎖定策略時, __exchange_and_add_dispatch 函式可能只保證原子性,但可能無法保證它是完全隔離的,對嗎?
是的。
- 并且因為 1,'__atomic_thread_fence (__ATOMIC_ACQ_REL)' 的目的是確保當執行緒 A 呼叫 _M_dispose 時,沒有執行緒會呼叫 _M_destory(因此執行緒 A 永遠無法訪問函式 '_M_dispose 中的銷毀成員(例如:_M_ptr) '?
不,這不可能發生。當_M_release_last_use()開始執行時_M_weak_count >= 1,_M_destroy()函式在_M_weak_count遞減之后才會被呼叫,因此_M_ptr在_M_dispose()呼叫期間保持有效。如果_M_weak_count==2另一個執行緒在_M_release_last_use()運行的同時遞減它,那么就會出現競爭以查看哪個執行緒先遞減,因此您不知道哪個執行緒將運行_M_destroy()。但是無論它是哪個執行緒,_M_dispose()在_M_release_last_use()它之前已經完成了它的遞減。
記憶體屏障的原因是_M_dispose()呼叫洗掉器,它運行用戶定義的代碼。該代碼可能具有任意副作用,觸及程式中的其他物件。該_M_destroy()函式使用operator delete或 分配器來釋放記憶體,這也可能產生任意副作用(如果程式已替換operator delete,或者shared_ptr是使用自定義分配器構造的)。記憶體屏障確保洗掉器的效果與釋放的效果同步。庫不知道這些效果是什么,但沒關系,代碼仍然確保它們同步。
- 最讓我困惑的是,如果 1 和 2 都正確,
他們不是。
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/382693.html
