1、概述
你知道條件變量"虛假喚醒"問題么,下面代碼有問題么
void CFileTaskThread::Run()
{while (!m_bStop){CFileItemRequest* pFileItem;{std::unique_lock<std::mutex> guard(m_mtItems);if (m_Filelist.empty()){if (m_bStop)return;// 等待條件滿足m_cvItems.wait(guard);}pFileItem = m_Filelist.front();m_Filelist.pop_front();}// do something}
}
2、分析
上述代碼,當列表?m_Filelist
?為空時,調用?wait
?進入阻塞狀態,等待條件變量被觸發。一旦其他線程通過?notify_one()
?或?notify_all()
?發出通知,wait
?函數解除阻塞并返回。隨后,從列表頭部取出一個任務繼續執行。
虛假喚醒:
一個線程正在調用std::condition_variable::wait()等待,沒有任何線程調用notify_one()或notify_all情況下,wait函數解除阻塞并返回了。
如果是虛假喚醒場景,那么上述代碼就會有問題,當wait函數返回后,準備從列表頭部取出一個任務,而此時列表是空的,程序就崩潰了。
正確寫法
循環中使用?while
?檢查條件可以防止虛假喚醒。即使線程被意外喚醒,while
?會再次檢查列表是否為空。如果列表仍然為空,線程會繼續調用?wait
?并保持等待狀態,直到條件真正滿足。
void CFileTaskThread::Run()
{while (!m_bStop){CFileItemRequest* pFileItem;{std::unique_lock<std::mutex> guard(m_mtItems);// 注意注意,這里用whilewhile (m_Filelist.empty()){if (m_bStop)return;// 等待條件滿足m_cvItems.wait(guard);}pFileItem = m_Filelist.front();m_Filelist.pop_front();}// do something}
}
學習鏈接:https://github.com/0voice