想象一下你正在组织一场多人接力赛。所有参赛选手需要同时起跑,每个选手完成自己的赛段后,必须等待其他选手都到达交接区才能开始下一轮。在C++多线程编程中,std::barrier就是那个确保所有线程"同步起跑"和"同步交接"的裁判员。
我第一次在图像处理项目中遇到这个问题:8个线程分别处理图片的不同区块,但必须等所有区块都完成锐化处理后才能开始降噪。当时用条件变量手写同步代码,结果出现了死锁。后来改用C++20的std::barrier,代码量减少了70%,性能反而提升了15%。
std::barrier内部维护着两个关键计数器:
当线程调用arrive_and_wait()时,会发生以下原子操作:
cpp复制#include <barrier>
#include <vector>
#include <thread>
// 模拟4个工人分阶段施工
std::barrier construction_barrier(4);
void construction_worker(int id) {
std::cout << "工人" << id << "开始地基施工\n";
construction_barrier.arrive_and_wait(); // 等待所有工人完成地基
std::cout << "工人" << id << "开始主体建造\n";
construction_barrier.arrive_and_wait(); // 等待所有工人完成主体
std::cout << "工人" << id << "开始内部装修\n";
}
int main() {
std::vector<std::thread> workers;
for (int i = 1; i <= 4; ++i) {
workers.emplace_back(construction_worker, i);
}
for (auto& w : workers) {
w.join();
}
}
| 方法 | 线程计数 | 典型场景 | 注意事项 |
|---|---|---|---|
| arrive_and_wait() | 保持不变 | 固定数量线程的多阶段同步 | 必须确保所有线程都会调用 |
| arrive_and_drop() | 自动减1 | 动态线程组(如异常退出情况) | 后续阶段需要调整预期值 |
cpp复制std::barrier adaptive_barrier(5); // 初始5个线程
void task(int id) {
try {
// 第一阶段工作
adaptive_barrier.arrive_and_wait();
if(id == 2) throw std::runtime_error("模拟异常");
// 第二阶段工作
adaptive_barrier.arrive_and_wait();
} catch (...) {
adaptive_barrier.arrive_and_drop(); // 该线程退出同步组
return;
}
}
我们来看一个真实的四阶段图像处理流程:
cpp复制constexpr int THREAD_NUM = 4;
std::barrier image_barrier(THREAD_NUM);
std::vector<ImageTile> tiles(THREAD_NUM);
void process_pipeline(int thread_id) {
// 阶段1:加载并解码分块
tiles[thread_id] = load_image_tile(thread_id);
image_barrier.arrive_and_wait();
// 阶段2:交叉验证色彩
verify_color(tiles[(thread_id+1)%THREAD_NUM]);
image_barrier.arrive_and_wait();
// 阶段3:并行特征提取
auto features = extract_features(tiles[thread_id]);
image_barrier.arrive_and_wait();
// 阶段4:汇总结果
if(thread_id == 0) {
merge_all_features(tiles);
}
}
cpp复制class BarrierGuard {
public:
explicit BarrierGuard(std::barrier& b) : barrier_(b) {}
~BarrierGuard() {
if(!std::uncaught_exceptions()) {
barrier_.arrive_and_wait();
} else {
barrier_.arrive_and_drop();
}
}
private:
std::barrier& barrier_;
};
void safe_worker(std::barrier& b) {
BarrierGuard bg(b); // 异常安全屏障
// ...工作代码...
}
线程数不匹配:
异常导致的线程退出:
递归调用屏障:
cpp复制struct PhaseTracker {
void arrive_and_wait(std::barrier& b, int phase) {
std::cout << "线程" << std::this_thread::get_id()
<< "到达阶段" << phase << std::endl;
b.arrive_and_wait();
}
};
cpp复制bool try_arrive_and_wait(std::barrier& b, std::chrono::milliseconds timeout) {
auto start = std::chrono::steady_clock::now();
while(b.arrive_count() > 0) {
if(std::chrono::steady_clock::now() - start > timeout) {
b.arrive_and_drop();
return false;
}
std::this_thread::yield();
}
return true;
}
cpp复制std::mutex io_mutex;
std::barrier io_barrier(4);
void synchronized_io(int id) {
{
std::lock_guard lock(io_mutex);
std::cout << "线程" << id << "开始IO操作\n";
}
io_barrier.arrive_and_wait();
{
std::lock_guard lock(io_mutex);
std::cout << "线程" << id << "继续后续操作\n";
}
}
cpp复制template<typename T>
std::vector<T> parallel_map_reduce(
const std::vector<T>& input,
auto map_func,
auto reduce_func)
{
const unsigned num_workers = std::thread::hardware_concurrency();
std::barrier reduce_barrier(num_workers);
std::vector<T> partial_results(num_workers);
std::vector<std::thread> workers;
auto worker_task = [&](unsigned worker_id) {
// Map阶段
auto chunk_begin = input.begin() + worker_id*input.size()/num_workers;
auto chunk_end = input.begin() + (worker_id+1)*input.size()/num_workers;
partial_results[worker_id] = map_func(chunk_begin, chunk_end);
reduce_barrier.arrive_and_wait();
// Reduce阶段(仅主线程执行)
if(worker_id == 0) {
return reduce_func(partial_results.begin(), partial_results.end());
}
return T{};
};
for(unsigned i = 0; i < num_workers; ++i) {
workers.emplace_back(worker_task, i);
}
// ...获取结果并返回...
}
在实现高性能计算框架时,这种模式可以将8核CPU的利用率提升到90%以上。我曾用类似方案将一批基因序列比对任务从原来的6小时缩短到47分钟。关键点在于合理设置屏障数量和确保每个阶段的工作量均衡。