所以我見過很多文章聲明在C++中雙重檢查鎖定,通常用於防止多線程嘗試初始化一個懶惰創建的單例,已被打破。普通雙檢查鎖定代碼讀取這樣的:這個修復程序對於雙重檢查鎖定有什麼問題?
class singleton {
private:
singleton(); // private constructor so users must call instance()
static boost::mutex _init_mutex;
public:
static singleton & instance()
{
static singleton* instance;
if(!instance)
{
boost::mutex::scoped_lock lock(_init_mutex);
if(!instance)
instance = new singleton;
}
return *instance;
}
};
的問題顯然是行分配實例 - 編譯器可以自由分配的對象,然後將指針分配給它,或者設定指針的地方將被分配,然後分配它。後一種情況會破壞成語 - 一個線程可能會分配內存並分配指針,但在其進入睡眠狀態之前不會運行單例的構造函數 - 然後第二個線程將看到該實例不爲null並嘗試返回它,儘管它還沒有建成。
我saw a suggestion使用線程本地布爾值,並檢查,而不是instance
。事情是這樣的:
class singleton {
private:
singleton(); // private constructor so users must call instance()
static boost::mutex _init_mutex;
static boost::thread_specific_ptr<int> _sync_check;
public:
static singleton & instance()
{
static singleton* instance;
if(!_sync_check.get())
{
boost::mutex::scoped_lock lock(_init_mutex);
if(!instance)
instance = new singleton;
// Any non-null value would work, we're really just using it as a
// thread specific bool.
_sync_check = reinterpret_cast<int*>(1);
}
return *instance;
}
};
這樣每個線程結束了,如果實例已經被創建一次檢查,但在此之後停止,這需要一定的性能損失,但仍沒有那麼糟糕,因爲每次調用鎖定。但是,如果我們只是使用本地靜態布爾呢?:
class singleton {
private:
singleton(); // private constructor so users must call instance()
static boost::mutex _init_mutex;
public:
static singleton & instance()
{
static bool sync_check = false;
static singleton* instance;
if(!sync_check)
{
boost::mutex::scoped_lock lock(_init_mutex);
if(!instance)
instance = new singleton;
sync_check = true;
}
return *instance;
}
};
爲什麼不能這樣工作?即使sync_check被另一個線程在另一個線程中分配時讀取,垃圾值仍然不爲零,因此也是如此。 This Dr. Dobb's article聲稱你必須鎖定,因爲你永遠不會因爲重新排序指令而與編譯器爭鬥。這讓我覺得這不應該出於某種原因,但我不明白爲什麼。如果序列點的要求像Dobb博士的文章讓我相信的那樣丟失,我不明白爲什麼鎖之後的任何代碼都不能重新排序爲在鎖之前。這將使C++多線程斷開期間。
我想我可以看到編譯器被允許特別重新排序sync_check在鎖之前,因爲它是一個局部變量(即使它是靜態的,我們沒有返回一個引用或指針) - 但是這樣仍然可以通過使其成爲靜態成員(有效全局)來解決。
那麼這項工作還是不會呢?爲什麼?
問題是變量可能在構造函數運行(或完成)之前分配,而不是在分配對象之前分配。 – kdgregory 2009-06-03 14:53:41
謝謝,糾正。我完全誤解了比賽的狀況。 – 2009-06-03 15:21:57
是的,你是對的,現在的C++確實是「多線程斷點」。只考慮標準。編譯器供應商通常會提供解決方法,因此實際結果並不那麼糟糕。 – Suma 2009-06-16 15:58:27