在多線程的環境下,有些時候我們不需要某給函數被調用多次或者某些變量被初始化多次,它們僅僅只需要被調用一次或者初始化一次即可。很多時候我們為了初始化某些數據會寫出如下代碼,這些代碼在單線程中是沒有任何問題的,但是在多線程中就會出現不可預知的問題。
[cpp] bool initialized = false; // global flag
if (!initialized) {
// initialize if not initialized yet
initialize ();
initialized = true;
}
or
static std::vector<std::string> staticData;
void foo ()
{
if (staticData.empty ()) {
staticData = initializeStaticData ();
}
...
}
bool initialized = false; // global flag
if (!initialized) {
// initialize if not initialized yet
initialize ();
initialized = true;
}
or
static std::vector<std::string> staticData;
void foo ()
{
if (staticData.empty ()) {
staticData = initializeStaticData ();
}
...
}
為了解決上述多線程中出現的資源競爭導致的數據不一致問題,我們大多數的處理方法就是使用互斥鎖來處理。在C++11中提供了最新的處理方法:使用std::call_once函數來處理,其定義如下頭文件#include<mutex>
[cpp] template< class Function, class... Args >
void call_once ( std::once_flag& flag, Function&& f, Args&& args... );
參數解析Parameters:
flag - an object, for which exactly one function gets executed
f - 需要被調用的函數
args... - 傳遞給函數f的參數(可以多個)
返回值為 (none)
拋出異常
std::system_error if any condition prevents calls to call_once from executing as specified any exception thrown by f
template< class Function, class... Args >
void call_once ( std::once_flag& flag, Function&& f, Args&& args... );
參數解析Parameters:
flag - an object, for which exactly one function gets executed
f - 需要被調用的函數
args... - 傳遞給函數f的參數(可以多個)
返回值為 (none)
拋出異常
std::system_error if any condition prevents calls to call_once from executing as specified any exception thrown by f
例:
[cpp] static std::vector<std::string> staticData;
std::vector<std::string>initializeStaticData ()
{
std::vector<std::string> vec;
vec.push_back ("initialize");
return vec;
}
void foo()
{
static std::once_flag oc;
std::call_once(oc, [] { staticData = initializeStaticData ();});
}
static std::vector<std::string> staticData;
std::vector<std::string>initializeStaticData ()
{
std::vector<std::string> vec;
vec.push_back ("initialize");
return vec;
}
void foo()
{
static std::once_flag oc;
std::call_once(oc, [] { staticData = initializeStaticData ();});
}正如上面的例子所示call_once函數第一個參數是std::once_flag的一個對象,第二個參數可以是函數、成員函數、函數對象、lambda函數。在實際的使用過程中,我們會經常將該函數用於延後初始化或者類的單例實現(Singleton)。
[cpp] class X {
private:
mutable std::once_flag initDataFlag;
void initData ()
{
_data = "init";
}
std::string _data;
public:
std::string& getData () {
std::call_once (initDataFlag, &X::initData, this);
return _data;
}
};
class X {
private:
mutable std::once_flag initDataFlag;
void initData ()
{
_data = "init";
}
std::string _data;
public:
std::string& getData () {
std::call_once (initDataFlag, &X::initData, this);
return _data;
}
};
關於異常Exception: 由被調用的函數產生出的異常都將會重新拋出,因此你最好有異常處理。如果第一次調用沒有成功的話,那麼第二次還會繼續調用,一次類推直到調用成功為止。
關於程序編譯:需要加 -std=c++0x或者-std=c++11和-lpthread選項