執行緒安全的 C 11 佇列:解決假執行緒喚醒
在多方面的項目中,多個執行緒同時處理檔案清單。每個線程都可以將文件添加到隊列中進行處理,這應該無縫運行並避免競爭條件。然而,出現了一些意外的分段錯誤,促使對其起源進行調查。
FileQueue 類別使用互斥體 (qMutex) 和條件變數 (populatedNotifier) 來協調執行緒之間的佇列操作。當線程將文件添加到隊列(入隊)時,它會向等待線程發出信號(populatedNotifier.notify_one()),當線程從隊列中檢索文件(出隊)時,它會等待隊列被填充(如果必要:populatedNotifier.wait_for())。
儘管採取了這些預防措施,出隊方法中偶爾會出現分段錯誤,特別是在if (...wait_for(lock, timeout) == std::cv_status:: no_timeout ) { } 塊。檢查代碼表明崩潰時隊列為空。這種行為是矛盾的,因為 wait_for 預計只會在收到通知時傳回 cv_status::no_timeout,這表示檔案已新增至佇列。
怎麼會發生這種莫名其妙的錯誤?
罪魁禍首:虛假喚醒
事實證明,由於程式無法控制的因素(例如係統中斷或重新安排),條件變數可能會經歷“虛假喚醒” 。發生這種情況時,即使監視的條件沒有實際變化,執行緒也可能會被喚醒。
在 FileQueue 出隊方法中,條件變數用來等待新檔案的到來。但是,由於條件是在鎖定釋放之後檢查的,因此在執行緒重新取得鎖定之前隊列可能會再次變空。因此,條件可能不再有效。
解決方案:逆條件和鎖定保護
更強大的基於條件變數的方法涉及將循環重構為使用逆條件並在整個操作過程中保持鎖定:
<code class="cpp">while (q.empty()) { populatedNotifier.wait(lock); }</code>
透過在釋放鎖定之前檢查空隊列,執行緒確保條件在整個關鍵時刻保持有效部分。如果被虛假喚醒,執行緒會在繼續之前重新檢查條件。
替代實現:非同步隊列的模板
本著線程安全隊列的精神實現,這裡是一個提供替代解決方案的模板:
<code class="cpp">while (q.empty()) { populatedNotifier.wait(lock); }</code>
此實作採用互斥體和條件變量,以及一個 while 循環,確保在鎖內檢查條件(空隊列)並在出現虛假喚醒時重新評估。
以上是儘管使用條件變量,為什麼我的線程安全 C 11 佇列中仍會出現分段錯誤?的詳細內容。更多資訊請關注PHP中文網其他相關文章!