任务完成计数模型
内容介绍
工程并发里,经常需要等待一组任务全部结束。C++ 可以用计数器、互斥锁和条件变量实现“任务完成计数”。
这个模型的性能意义是:主线程阻塞等待,不忙等;工作线程完成后只做一次轻量通知。
简化完成计数器
#include <condition_variable>
#include <iostream>
#include <mutex>
#include <thread>
#include <vector>
class WaitGroup {
public:
void add(int n) {
std::lock_guard<std::mutex> lock(m_);
count_ += n;
}
void done() {
{
std::lock_guard<std::mutex> lock(m_);
--count_;
}
cv_.notify_all();
}
void wait() {
std::unique_lock<std::mutex> lock(m_);
cv_.wait(lock, [&] {
return count_ == 0;
});
}
private:
std::mutex m_;
std::condition_variable cv_;
int count_ = 0;
};
int main() {
WaitGroup wg;
std::vector<std::thread> threads;
for (int i = 0; i < 4; ++i) {
wg.add(1);
threads.emplace_back([&, i] {
std::cout << "task " << i << '\n';
wg.done();
});
}
wg.wait();
for (auto& t : threads) {
t.join();
}
}最佳代码实践
add要在线程启动前完成,避免wait提前返回。done最好用 RAII guard 包起来,避免异常导致计数不归零。- 完成计数器只负责等待,不负责传递任务和结果。
- 需要结果时结合 cppfuture和promise 或 cppChannel模型。
常见错误用法
std::thread([&] {
wg.add(1); // 错误倾向:在线程内部 add
do_work();
wg.done();
}).detach();
wg.wait();问题:主线程可能先执行 wait,看到计数为 0 后直接返回。
注意事项
- 教学版完成计数器没有防止计数变成负数。
- 工程实现应检查非法
done、重复等待、异常路径。 - C++20 也可以用
std::latch/std::barrier解决部分等待场景。
学习路径
- 上一节:cppChannel模型
- 当前阶段:cpp多线程
- 下一节:cpp线程池