OpenMP并行素数程序数组数据异常问题求助
OpenMP素数程序数组数据错乱的问题分析与解决
你的问题核心是竞争条件(Race Condition),根源出在未加保护的共享变量counter上。
看这段并行执行的代码:
#pragma omp parallel for shared(arr) for (int i = 0; i <= n; i++) { if (isPrime(i)) { arr[counter] = i; printf("%2d ", arr[counter]); counter++; } }
counter是所有线程共享的全局变量,但你没有给它加任何同步保护。多个线程会同时读取counter的当前值,执行arr[counter] = i时,会出现多个线程把不同素数写入数组同一位置的情况;紧接着的counter++也不是原子操作,多个线程的递增操作会互相干扰,最终导致数组索引完全混乱,counter的最终值也会和实际素数数量不符。
第一次打印看起来正常,是因为每个线程找到素数后直接打印了结果,和数组写入逻辑无关;但数组里的内容已经因为竞争被写乱,第二次打印自然就会出现错误数据。
解决方法
方法1:用临界区保护共享操作(简单直接)
把写入数组和递增counter的逻辑放进临界区,确保同一时间只有一个线程执行这段代码:
#pragma omp parallel for shared(arr, counter) for (int i = 0; i <= n; i++) { if (isPrime(i)) { #pragma omp critical { arr[counter] = i; printf("%2d ", arr[counter]); counter++; } } }
这种方式会有一定性能开销,因为线程会在临界区排队等待,但对于入门场景足够实用。
方法2:无竞争的分块处理(性能更优,推荐)
让每个线程独立处理自己负责的数值范围,先把素数存在私有缓冲区,最后再合并到全局数组,完全避免竞争:
// compile: gcc primes.c -fopenmp -o primes #include <stdio.h> #include <stdlib.h> #include <omp.h> int isPrime(int n) { if (n < 2) return 0; for (int i = 2; i*i <= n; i++) { // 优化:循环到sqrt(n)即可 if (n % i == 0) return 0; } return 1; } int main(int argc, char *argv[]) { int n; if (argc < 2 || !(n = atoi(argv[1]))) return 0; // 第一步:统计素数总数,方便分配全局数组 int total_primes = 0; #pragma omp parallel reduction(+:total_primes) { #pragma omp for for (int i = 0; i <= n; i++) { if (isPrime(i)) total_primes++; } } int *arr = (int *)malloc(sizeof(int) * total_primes); int counter = 0; // 第二步:分块并行计算素数 #pragma omp parallel { int thread_id = omp_get_thread_num(); int num_threads = omp_get_num_threads(); // 计算当前线程负责的数值范围 int start = thread_id * ((n + 1) / num_threads); int end = (thread_id == num_threads - 1) ? n : start + ((n + 1) / num_threads) - 1; // 线程私有缓冲区 int local_count = 0; int *local_arr = (int *)malloc(sizeof(int) * (end - start + 1)); // 独立计算当前范围的素数 for (int i = start; i <= end; i++) { if (isPrime(i)) { local_arr[local_count++] = i; } } // 合并到全局数组 #pragma omp critical { for (int i = 0; i < local_count; i++) { arr[counter++] = local_arr[i]; } } free(local_arr); } // 打印结果 for (int i = 0; i < total_primes; i++) { printf("%2d ", arr[i]); } printf("\n"); free(arr); return 0; }
另外,isPrime函数里的循环可以优化到sqrt(n),能大幅减少不必要的计算量。
额外说明
你用静态数组或动态malloc都出问题,是因为问题根源不在数组的分配方式,而是共享变量的竞争逻辑。不管数组存在栈上还是堆上,只要多个线程同时无保护地修改索引,都会导致数据错乱。
内容的提问来源于stack exchange,提问作者motaylormo
相关产品推荐
相关产品推荐

