正确使用#pragma omp parallel for解决QVector填充性能问题
问题根因
- 不加
#pragma omp critical就崩溃的原因:QVector的push_back方法不是线程安全的。多个线程同时对同一个QVector执行写入操作时会触发内存竞争:比如两个线程同时触发容器扩容、同时修改容器内部的长度计数,直接破坏内存结构,程序自然无法正常运行。 - 加了临界区之后多线程反而更慢的原因:临界区的特性是同一时间只允许一个线程进入执行,你把结果判断、写入容器的逻辑全放在临界区里,等于所有线程算完y值之后都要排队等锁,挨个串行执行写入操作。再加上线程调度、加锁解锁本身的固定开销,整体速度比单线程直接跑慢是必然结果。
简单易上手的解决方法
不要让多个线程直接写共享的x、y容器,改成每个线程先存自己的计算结果,所有计算完成后再统一合并到总容器里,全程不需要加锁,性能损失极小。
参考改法:
#include <omp.h> #include <vector> omp_set_num_threads(2); // 按线程数预分配私有存储,每个线程只操作自己对应的数组,完全无冲突 const int thread_cnt = omp_get_max_threads(); std::vector<std::vector<double>> thread_private_x(thread_cnt); std::vector<std::vector<double>> thread_private_y(thread_cnt); #pragma omp parallel for for (int current = start; current < finish; current++) { Calc b(a.get_string()); double cur_x = (double)current / 1000; double y_value = b.parsing(cur_x); if (y_value > yL || y_value < yR) { int tid = omp_get_thread_num(); // 获取当前线程的编号 thread_private_x[tid].push_back(cur_x); thread_private_y[tid].push_back(y_value); } } // 所有并行计算结束后,单线程合并所有线程的结果 x->reserve(finish - start); // 提前预留足够内存,避免push_back频繁扩容 y->reserve(finish - start); for (int i = 0; i < thread_cnt; i++) { x->insert(x->end(), thread_private_x[i].begin(), thread_private_x[i].end()); y->insert(y->end(), thread_private_y[i].begin(), thread_private_y[i].end()); }
额外新手提示
- 不管是STL容器还是Qt容器,默认都不支持多线程同时写入,只要涉及多线程写共享数据,要么加锁做同步,要么用“线程私有计算+最终合并”的方案,后者性能远高于频繁加锁。
- 不要给计算量太小的循环强行加并行:如果单次循环的计算耗时比线程调度、上下文切换的开销还小,多线程只会越跑越慢。你这个场景有表达式解析逻辑,计算量足够,用对并行方法是能拿到明显提速的。
- 如果一定要用临界区,尽量把临界区内的代码压缩到最少,不要把非必要的计算逻辑放进临界区,不然锁等待的开销会吃掉所有并行带来的收益。
内容的提问来源于stack exchange,提问作者Genrih Frank
相关产品推荐
相关产品推荐

