如果你有一個C++ 11兼容的編譯器
(例如,不VS2013)
兩個,最簡單,最有效的方法是隻寫:
int f(int x) {
static int firstArg = x;
return firstArg*x;
}
C++ 11標準要求初始化函數本地靜態變量是線程安全的*)。更確切地說,它要求只有一個線程初始化變量,並且所有其他線程都等待,直到初始化完成(稍後的讀寫操作當然可以競賽,但由於這是對firstArg
的唯一寫訪問,這裏需要同步)。
如果你的編譯器不支持「神奇靜」
下一個最好的方法是使用std::call_once
由塞巴斯蒂安·雷德爾,它具有相同的語義建議。
如果通過std::call_once
初始化太慢了(它可能使用一個互斥體)和arg
是一個內置類型(如int),你可以試試下面的(我沒有做任何測量):
namespace {
const int DISALLOWED_VALUE = std::numeric_limits<int>::max();
std::atomic<int> firstArg= DISALLOWED_VALUE;
}
int f(int x) {
if (firstArg.load(std::memory_order_relaxed) == DISALLOWED_VALUE) {
int tmp = DISALLOWED_VALUE;
firstArg.compare_exchange_strong(tmp, x);
}
return firstArg.load(std::memory_order_relaxed)*x;
}
DISALLOWED_VALUE
是一些不可能作爲有效參數傳遞給f的值。在這種情況下,std::numeric_limits<int>::max()
會在與自身相乘時導致整數溢出,因此它不是f
的有效參數,因此可以用作firstArg
尚未初始化的指示符。
警告:只使用這個,如果你已經驗證,即std::call_once
是太慢了具體的工作負載(這幾乎不可能出現的情況),而且這個版本實際上是一個充分的改善。有條件鎖定
由於有
注意/人所提出的各種故障條件鎖定算法一些答案,我也提出了一個正確的手動實現雙重檢查鎖定。
namespace {
std::atomic<bool> isInit = false; //has to be atomic
std::mutex mux;
}
int f(int x) {
static int firstArg;
if (!isInit.load(std::memory_order_acquire)) {
std::lock_guard<std::mutex> lg(mux);
if (!isInit.load(std::memory_order_acquire)) {
firstArg = x;
isInit.store(true,std::memory_order_release);
}
}
return firstArg*x;
}
兩個重要的部分是:
- 做雙重檢查(一旦進入,一旦保護區外)
- 使用
std::atomic
爲標誌。否則,無法保證不鎖定的線程遵守商店的標誌和變量的順序。
致謝:
的雙重檢查鎖版是基於cppcon2014呈現由香草薩特和基於由EOF和塞巴斯蒂安的意見/答案的範圍得到擴大。
*)參見例如, this question和從c 14標準(6.7點4)的++最新工作草案:
如果控制進入聲明同時而可變正在初始化,併發執行應等待初始化的完成。
看看pthread_once。另請參閱C11/C++ 11原子,以獲得更快的標誌防護。 – Jeff
請縮進您的代碼。 –
C或C++?他們有不同的實現。 – edmz