一、題目解讀
力扣1116題要求設計一個類,實現三個線程交替輸出數字:一個線程輸出連續的0,一個線程輸出連續的偶數,另一個線程輸出連續的奇數。輸入參數n為總輸出次數(每個線程各輸出n次),輸出需嚴格按照0-偶數-奇數的順序循環,直至所有線程完成指定次數。題目核心在于多線程間的精確協作與同步控制。
二、解題思路
通過條件變量與互斥鎖實現線程間的同步。關鍵在于設計狀態變量next_type(標記下一個輸出類型)與count(全局計數),結合cv.wait()的謂詞條件判斷線程喚醒時機。通過notify_all()確保所有等待線程重新檢查條件,避免死鎖或饑餓問題。
三、步驟解析
1. 初始化:構造函數中初始化n、count=1、zero_flag=true、next_type=0,確保首個輸出為0。
2. zero線程:循環n次,每次等待next_type=0,輸出0后更新next_type為后續類型(奇數或偶數),并通知其他線程。
3. even/odd線程:循環直至count > n或獲取對應類型權限。通過謂詞條件判斷是否激活(如偶數線程等待next_type=2或結束條件),輸出并更新狀態,最后通知所有線程。
4. 同步邏輯:利用unique_lock自動管理鎖,條件變量結合謂詞避免虛假喚醒,notify_all()保證所有線程重新評估條件。
四、代碼與注釋
class ZeroEvenOdd {
private:int n; // 總輸出次數int count; // 全局計數bool zero_flag; // 初始標記int next_type; // 0: zero, 1: odd, 2: evenstd::mutex mtx; // 互斥鎖std::condition_variable cv; // 條件變量public:ZeroEvenOdd(int n) {this->n = n; // 初始化參數count = 1; // 從1開始計數zero_flag = true; // 首個線程為0next_type = 0; // 初始輸出類型}void zero(function<void(int)> printNumber) {for (int i = 0; i < n; ++i) { // 循環n次unique_lock<mutex> lock(mtx); // 加鎖cv.wait(lock, [this]{ return next_type == 0; }); // 等待類型為0printNumber(0); // 輸出0next_type = (count % 2 == 1)? 1 : 2; // 根據count奇偶決定后續類型cv.notify_all(); // 喚醒所有線程}}void even(function<void(int)> printNumber) {while (true) {unique_lock<mutex> lock(mtx);cv.wait(lock, [this]{ return next_type == 2 || count > n; // 等待類型為2或結束條件});if (count > n) break; // 結束循環printNumber(count++); // 輸出偶數并遞增計數next_type = 0; // 重置類型cv.notify_all();}}void odd(function<void(int)> printNumber) {while (true) {unique_lock<mutex> lock(mtx);cv.wait(lock, [this]{ return next_type == 1 || count > n; });if (count > n) break;printNumber(count++);next_type = 0;cv.notify_all();}}
};
五、總結
該解法巧妙利用條件變量與互斥鎖實現線程間的精確協作,通過狀態變量動態切換輸出類型,避免復雜的條件判斷。核心優勢在于:
1. 清晰的狀態轉移邏輯(next_type與count的協同);
2. 謂詞條件防止虛假喚醒,提升效率;
3. notify_all()確保所有線程響應,避免饑餓問題。
對多線程同步控制與力扣算法優化具有參考價值,適用于需要高并發協作的場景。
來源:力扣題解