只調(diào)用一次
有些功能我們只需要或者必須只調(diào)用一次鳍咱,在單線程環(huán)境下,通過判斷一個bool flag即可:
bool g_inited;
int main()
{
if (!g_inited) {
g_inited = true;
//...init
}
system("pause");
}
而在多線程環(huán)境下断医,這些只需要調(diào)用一次的功能有可能會被多次調(diào)用廷雅,你可以使用mutex來避免多次調(diào)用,但C++標(biāo)準(zhǔn)庫提供了一種更方便的方法腌乡,下面的init函數(shù)只會被調(diào)用一次盟劫。
call_once調(diào)用的函數(shù)如果發(fā)生異常會被call_once拋出,并且這次調(diào)用被視為不成功导饲,下一次call_once還可以再調(diào)用函數(shù)捞高。
once_flag initFlag;
void init()
{
cout << "init" << endl;
}
void threadProc()
{
call_once(initFlag, init);
}
int main()
{
thread(threadProc).detach();
thread(threadProc).detach();
thread(threadProc).detach();
thread(threadProc).detach();
system("pause");
}
條件變量
有時候不同線程之間需要彼此等待對方的數(shù)據(jù)才能進行下一步操作氯材,最簡單粗暴的方法是輪詢是否有數(shù)據(jù),期間還要使用mutex進行同步硝岗,這種方式效率很低氢哮,更好的辦法是使用C++標(biāo)準(zhǔn)庫中的condition_variable來同步線程之間的數(shù)據(jù)流邏輯依賴關(guān)系。
上述問題最典型的場景是生產(chǎn)者-消費者模型型檀,當(dāng)生產(chǎn)者產(chǎn)生數(shù)據(jù)后冗尤,調(diào)用notify_one方法喚醒等待中的線程,或調(diào)用notify_all方法喚醒所有等待中的線程胀溺,而消費者調(diào)用wait方法等待裂七。
為了等待condition_variable,你需要一個mutex和一個unique_lock用來同步wait仓坞,wait內(nèi)部會鎖定或解鎖mutex背零,所有等待某個condition_variable的線程都必須使用相同的mutex。
condition_variable有可能存在假醒的情況无埃,因此等待完成后不一定代表有數(shù)據(jù)產(chǎn)生了徙瓶,你需要再去驗證一次是否有數(shù)據(jù)。
condition_variable的wait方法總是在鎖住的mutex基礎(chǔ)上操作嫉称,調(diào)用wait方法時侦镇,會解鎖mutex然后進入等待狀態(tài),等到數(shù)據(jù)后會再次鎖定mutex织阅。如果要指定等待時間壳繁,可以使用wait_for,wait_until荔棉。
下面是一個簡單的生產(chǎn)者-消費者代碼闹炉,provider每隔200毫秒push一個數(shù)據(jù)到隊列,完成后調(diào)用notify_one喚醒消費者線程江耀,消費者線程等待喚醒后輸出數(shù)據(jù)剩胁。
#include <condition_variable>
using namespace std;
queue<int> g_queue;
mutex g_mutex;
condition_variable g_queueConditionVariable;
void provider(int baseNumber)
{
for (int i = 0; i < 10; ++i) {
{
lock_guard<mutex> lockGuard(g_mutex);
g_queue.push(baseNumber + i);
}
g_queueConditionVariable.notify_one();
this_thread::sleep_for(chrono::microseconds(200));
}
}
void consumer(int id)
{
while (true) {
unique_lock<mutex> uniqueLock(g_mutex);
g_queueConditionVariable.wait(uniqueLock, [] {return !g_queue.empty(); });
cout << "consumer" << id << ":" << g_queue.front() << endl;
g_queue.pop();
}
}
int main()
{
thread(provider, 1).detach();
thread(provider, 10).detach();
thread(provider, 100).detach();
thread(consumer, 1).detach();
thread(consumer, 2).detach();
cin.get();
}