如何在C++中实现三个线程按t1→t2→t3顺序同步执行?
实现线程严格按t1→t2→t3顺序执行的正确方案
基于Stack Overflow的一篇帖子延伸,原帖实现了t1 & t2并行执行完成后再执行t3的逻辑。现需求为让三个线程严格按t1全部执行完毕→t2全部执行完毕→t3全部执行完毕的顺序执行(系统可同时运行其他线程)。
以下是错误实现的最小可复现代码,以及期望输出和实际错误输出:
错误实现代码
Header.h
#include<thread> #include<mutex> #include<iostream> #include <condition_variable>
MultiClass.h
#include "Header.h" #include "SynchObj.h" class MultiClass { public: void Run() { std::thread t1(&MultiClass::Calc1, this); std::thread t2(&MultiClass::Calc2, this); std::thread t3(&MultiClass::Calc3, this); t1.join(); t2.join(); t3.join(); } private: SyncObj obj; void Calc1() { for (int i = 0; i < 10; ++i) { obj.waitForCompletionOfT3(); std::cout << "T1:" << i << std::endl; obj.signalCompletionOfT1(); } } void Calc2() { for (int i = 0; i < 10; ++i) { obj.waitForCompletionOfT1(); std::cout << "T2:" << i << std::endl; obj.signalCompletionOfT2(); } } void Calc3() { for (int i = 0; i < 10; ++i) { obj.waitForCompletionOfT2(); std::cout << "T3:" << i << std::endl; obj.signalCompletionOfT3(); } } };
SynchObj.h
#include "Header.h" class SyncObj { std::mutex mux; std::condition_variable cv; bool completed[3]{ false, false, false }; public: /***** Original (t1 & t2) --> t3 *****/ /* void signalCompetionT1T2(int id) { std::lock_guard<std::mutex> ul(mux); completed[id] = true; cv.notify_all(); } void signalCompetionT3() { std::lock_guard<std::mutex> ul(mux); completed[0] = false; completed[1] = false; cv.notify_all(); } void waitForCompetionT1T2() { std::unique_lock<std::mutex> ul(mux); cv.wait(ul, [&]() {return completed[0] && completed[1]; }); } void waitForCompetionT3(int id) { std::unique_lock<std::mutex> ul(mux); cv.wait(ul, [&]() {return !completed[id]; }); } */ /***********************************/ /*** Unsuccessful attempt at t1 --> t2 --> t3 ***/ void signalCompletionOfT1() { std::lock_guard<std::mutex> ul(mux); completed[0] = true; cv.notify_all(); } void signalCompletionOfT2() { std::lock_guard<std::mutex> ul(mux); completed[0] = false; completed[1] = true; cv.notify_all(); } void signalCompletionOfT3() { std::lock_guard<std::mutex> ul(mux); completed[0] = false; completed[1] = false; completed[2] = true; cv.notify_all(); } void waitForCompletionOfT1() { std::unique_lock<std::mutex> ul(mux); cv.wait(ul, [&]() {return !completed[2]; }); } void waitForCompletionOfT2() { std::unique_lock<std::mutex> ul(mux); cv.wait(ul, [&]() {return !completed[0]; }); } void waitForCompletionOfT3() { std::unique_lock<std::mutex> ul(mux); cv.wait(ul, [&]() {return !completed[1]; }); } };
Source.cpp
#include "Header.h" #include "MultiClass.h" int main() { MultiClass m; m.Run(); return 0; }
期望输出
T1:0 T1:1 T1:2 T1:3 T1:4 T1:5 T1:6 T1:7 T1:8 T1:9 T2:0 T2:1 T2:2 T2:3 T2:4 T2:5 T2:6 T2:7 T2:8 T2:9 T3:0 T3:1 T3:2 T3:3 T3:4 T3:5 T3:6 T3:7 T3:8 T3:9
实际错误输出示例
0 T2:1 T2:2 T2:3 T2:4 T2:5 T2:6 T2:7 T2:8 T2:9
错误原因分析
原实现的核心问题在于SyncObj的等待与信号逻辑完全错位:
- 初始状态下
completed[2]为false,T1的waitForCompletionOfT3()直接通过,但T1每次循环的等待条件和T3是否完成无关,逻辑混乱。 T2的等待条件判断的是!completed[2],这和T1是否完成没有关联,导致T2可能在T1未完成时就开始执行。- 信号函数中错误修改其他线程的状态标记,比如
signalCompletionOfT2()将completed[0]设为false,会导致T1可以再次进入循环,破坏了"t1全部执行完再执行t2"的核心需求。
正确实现方案
要实现t1全部执行完毕后t2才开始,t2全部执行完毕后t3才开始,我们可以用阶段标记+条件变量的方式,跟踪当前允许执行的线程阶段,具体实现如下:
修改后的SynchObj.h
#include "Header.h" class SyncObj { std::mutex mux; std::condition_variable cv; // 0: 允许T1执行,1: 允许T2执行,2: 允许T3执行 int allowed_stage = 0; public: // T1执行完成后,切换到允许T2执行的阶段 void signalT1Done() { std::lock_guard<std::mutex> ul(mux); allowed_stage = 1; cv.notify_all(); } // T2等待T1执行完成 void waitForT1Done() { std::unique_lock<std::mutex> ul(mux); cv.wait(ul, [this]() { return allowed_stage == 1; }); } // T2执行完成后,切换到允许T3执行的阶段 void signalT2Done() { std::lock_guard<std::mutex> ul(mux); allowed_stage = 2; cv.notify_all(); } // T3等待T2执行完成 void waitForT2Done() { std::unique_lock<std::mutex> ul(mux); cv.wait(ul, [this]() { return allowed_stage == 2; }); } };
修改后的MultiClass.h
#include "Header.h" #include "SynchObj.h" class MultiClass { public: void Run() { std::thread t1(&MultiClass::Calc1, this); std::thread t2(&MultiClass::Calc2, this); std::thread t3(&MultiClass::Calc3, this); t1.join(); t2.join(); t3.join(); } private: SyncObj obj; void Calc1() { for (int i = 0; i < 10; ++i) { std::cout << "T1:" << i << std::endl; } // T1全部执行完毕,通知T2可以开始 obj.signalT1Done(); } void Calc2() { // 等待T1全部执行完成 obj.waitForT1Done(); for (int i = 0; i < 10; ++i) { std::cout << "T2:" << i << std::endl; } // T2全部执行完毕,通知T3可以开始 obj.signalT2Done(); } void Calc3() { // 等待T2全部执行完成 obj.waitForT2Done(); for (int i = 0; i < 10; ++i) { std::cout << "T3:" << i << std::endl; } } };
方案说明
- 用
allowed_stage变量标记当前允许执行的线程阶段,初始值为0(允许T1执行)。 - T1无需等待,直接执行完所有循环后,将阶段切换为1并通知所有等待的线程,T2收到通知后开始执行。
- T2执行完所有循环后,将阶段切换为2并通知,T3收到通知后开始执行。
- 这种方式严格保证了
t1全部执行完毕后t2才启动,t2全部执行完毕后t3才启动,完全符合需求。
内容的提问来源于stack exchange,提问作者barak
相关产品推荐
相关产品推荐

