OpenMP任务间能否共享变量?变量更新未同步问题求助
OpenMP任务变量同步问题及目标场景实现方案
你的代码问题分析
你提供的代码存在三个关键问题导致输出不符合预期:
- 无效的OpenMP指令:
#pragma omp shared(currentInt)是错误指令,该位置需要创建新的并行task,否则两段代码会在同一个task内串行执行,无法实现并行测试。 - 变量可见性优化:
currentInt未声明为volatile,编译器会将其缓存到寄存器,导致其他线程无法及时读取更新后的值,即便任务完成也可能读取到初始的0。 - 任务并行性未正确实现:两段代码写在同一个task内,会按顺序执行,
Helper部分只会在Kruskals循环结束后运行,和你测试任务间同步的初衷不符。
修正后的测试代码
以下代码修正了上述问题,可验证变量同步效果:
#include <stdio.h> #include <stdlib.h> #include <omp.h> int main(int argc, char* argv[]) { int nThreads = atoi(argv[1]); omp_set_num_threads(nThreads); // 用volatile避免编译器优化,保证变量可见性 volatile int currentInt = 0; int numEdges = 1000000; #pragma omp parallel shared(currentInt) { #pragma omp single { // 创建第一个并行任务 #pragma omp task shared(currentInt) { printf("I am doing kruskals: Thread %d\n", omp_get_thread_num()); while(currentInt < numEdges) { currentInt++; } printf("Kruskals Done! %d\n", currentInt); } // 创建第二个并行任务,与第一个任务并行执行 #pragma omp task shared(currentInt) { // 模拟耗时操作 for(int i = 0; i < 10000000; i++){} printf("Helper: Current Int %d Thread %d\n", currentInt, omp_get_thread_num()); } #pragma omp taskwait } } return 0; }
目标场景的实现方案
针对「顺序遍历大数组+多并行任务处理子区域,顺序任务到达时并行任务停止」的需求,可按以下方式实现:
核心要点
- 变量可见性保障:用
volatile修饰共享的遍历进度变量,确保线程能实时读取最新值;若有多个线程修改进度变量,需用#pragma omp atomic保证原子性,避免竞争。 - 任务划分与停止逻辑:将数组划分为多个子区域,每个并行任务负责一个区域,处理时定期检查遍历进度,一旦进度覆盖当前区域则停止。
示例实现代码
#include <stdio.h> #include <omp.h> #define ARRAY_SIZE 1000000 #define NUM_TASKS 4 // 共享的遍历进度变量 volatile int traverse_pos = 0; int big_array[ARRAY_SIZE]; // 初始化数组 void init_array() { for(int i=0; i<ARRAY_SIZE; i++){ big_array[i] = i; } } // 并行任务:处理指定区间,遍历进度覆盖区间时停止 void process_chunk(int start, int end) { int tid = omp_get_thread_num(); printf("Task %d starts processing chunk [%d, %d]\n", tid, start, end); for(int i=start; i<end; i++){ // 检查是否需要停止 if(traverse_pos > i){ printf("Task %d stops at index %d, traverse_pos is %d\n", tid, i, traverse_pos); break; } // 模拟数组处理操作 big_array[i] *= 2; } } int main() { init_array(); omp_set_num_threads(NUM_TASKS); #pragma omp parallel shared(traverse_pos) { #pragma omp single { // 启动多个并行任务处理数组块 int chunk_size = ARRAY_SIZE / NUM_TASKS; for(int i=0; i<NUM_TASKS; i++){ int start = i * chunk_size; int end = (i == NUM_TASKS-1) ? ARRAY_SIZE : (i+1)*chunk_size; #pragma omp task process_chunk(start, end); } // 顺序遍历数组的任务 printf("Start traversing array...\n"); for(traverse_pos=0; traverse_pos<ARRAY_SIZE; traverse_pos++){ // 模拟遍历操作(如检查、记录) if(traverse_pos % 100000 == 0){ printf("Traversed to position: %d\n", traverse_pos); } } printf("Traversal done!\n"); #pragma omp taskwait } } return 0; }
关键说明
- 若只有顺序任务修改
traverse_pos,无需原子操作;若多线程修改,必须用#pragma omp atomic write更新。 - 可调整检查
traverse_pos的频率(比如每处理100个元素检查一次),平衡同步开销和响应速度。 - OpenMP的task会自动分配到空闲线程,无需手动绑定线程。
内容的提问来源于stack exchange,提问作者Andrew Van Hoveln
相关产品推荐
相关产品推荐

