如何用C++和OpenMP实现数组并行求和并修正结果异常问题
问题原因分析
- 现有代码的核心错误是仅用
#pragma omp parallel启动了多线程,但没有对循环做任务拆分:每个线程都会完整执行全部256次循环迭代,既会出现多个线程同时修改sinTable同一位置的数据竞争,也会导致同一个元素被重复计算、多次累加到sum中,最终结果必然错误且不稳定。 - 此前的调整没有解决循环任务拆分的核心问题,所以无论怎么修改sum的累加逻辑都无法得到正确结果。
符合要求的正确实现
要求仅使用critical、master、barrier、single四种OpenMP构造,我们可以手动拆分循环迭代范围,每个线程只负责处理自己分到的部分,再用局部变量缓存单线程求和结果后合并到全局sum,避免频繁加锁:
#include <iostream> #include <cmath> #include <omp.h> using namespace std; int main() { const int size = 256; double* sinTable = new double[size]; double sum = 0.0; #pragma omp parallel { // 获取当前线程ID和总线程数 int thread_id = omp_get_thread_num(); int total_threads = omp_get_num_threads(); // 手动计算当前线程负责的迭代范围 int chunk_size = size / total_threads; int start = thread_id * chunk_size; // 最后一个线程处理剩余所有元素,兼容size不是总线程数整数倍的场景 int end = (thread_id == total_threads - 1) ? size : start + chunk_size; // 用局部sum缓存当前线程的求和结果,避免每次循环都加锁 double local_sum = 0.0; for (int n = start; n < end; n++) { sinTable[n] = std::sin(2 * M_PI * n / size); sinTable[n] *= 2; local_sum += sinTable[n]; } // 局部sum计算完成后,用critical保证全局sum累加的线程安全 #pragma omp critical sum += local_sum; } cout << "Sum: " << sum << endl; delete[] sinTable; return 0; }
实现说明
- 手动拆分迭代范围的方案保证了每个
sinTable的下标只会被一个线程修改,完全消除了数组写入的数据竞争 - 局部变量缓存单线程求和结果的设计,仅最后合并时加一次
critical锁,既保证了求和的正确性,也比每次循环加锁的性能高很多 - 代码未使用未学习的OpenMP构造,符合要求
- 运行后会得到和预期一致的
4.15362e-14左右的结果(浮点数精度带来的极小波动属于正常情况)
内容的提问来源于stack exchange,提问作者Brianna Drew
相关产品推荐
相关产品推荐

