调用MPI_Waitall等待非阻塞接收时出现0xc0000005错误
问题根因
错误码0xc0000005是Windows平台的内存访问违例错误,由两处直接的内存非法操作触发,另有一处潜在逻辑缺陷:
- 第一处致命错误:非0进程的
MPI_Send调用参数非法
MPI_Send的第一个参数必须是指向待发送数据内存块的指针,原代码直接传入整数字面量1,等价于尝试从内存地址0x00000001读取数据发送,该地址属于系统保护区,访问直接触发违例。 - 第二处致命错误:0号进程接收缓冲区偏移越界
0号进程申请的data数组长度为process_count-1,合法索引范围是0 ~ process_count-2。但循环中接收非0进程i的数据时,直接使用data+i作为缓冲区首地址:当i遍历到process_count-1时,地址data + (process_count-1)已经超出malloc申请的内存范围,MPI写入接收数据时会触发堆溢出。 - 潜在逻辑缺陷:非0进程的
recvGrid()如果没有显式调用阻塞接收逻辑匹配0号进程tag为0的数独网格发送请求,0号进程等待发送请求完成的MPI_Waitall会永久阻塞;另外原代码0号进程仅处理链表中剩余的1个任务,若总任务数大于进程数会出现任务遗漏。
修复方案
- 非0进程发送完成标记时,先定义整型变量存储发送值,传入变量地址给MPI_Send
- 修正0号进程Irecv的缓冲区偏移,使用
data + (i-1)匹配request数组的索引 - 显式实现非0进程的数独网格接收逻辑,匹配0号的发送请求
- 增加链表遍历的非空校验,将单任务处理改为循环处理所有0号进程待执行的剩余任务
- 补充遗漏的动态内存释放逻辑,避免内存泄漏
修复后核心代码
if (rank == 0) { grids* sudokuList = initParallel(process_count); int* data = (int*)malloc((process_count - 1) * sizeof(int)); MPI_Request* recvRequests = (MPI_Request*)malloc((process_count - 1) * sizeof(MPI_Request)); MPI_Request* sendRequests = (MPI_Request*)malloc((process_count - 1) * sizeof(MPI_Request)); grids* ptr = sudokuList; for (int i = 1; i < process_count; i++) { // 增加空指针校验,避免链表长度不足时空指针访问 if (ptr == NULL) break; MPI_Isend(ptr->grid.sudoku, N * N, MPI_INT, i, 0, MPI_COMM_WORLD, &sendRequests[i - 1]); // 修正缓冲区偏移,和request索引对齐 MPI_Irecv(data + (i-1), 1, MPI_INT, i, 2, MPI_COMM_WORLD, &recvRequests[i - 1]); ptr = ptr->next; } // 循环处理0号进程分配后剩余的所有数独求解任务 while (ptr != NULL) { lookForSolution(ptr->grid, rank); ptr = ptr->next; } MPI_Status* sendStatuses = (MPI_Status*)malloc((process_count - 1) * sizeof(MPI_Status)); MPI_Waitall(process_count - 1, sendRequests, sendStatuses); MPI_Status* statuses = (MPI_Status*)malloc((process_count - 1) * sizeof(MPI_Status)); MPI_Waitall(process_count - 1, recvRequests, statuses); // 统一释放所有动态申请的资源 free(recvRequests); free(sendStatuses); free(sendRequests); free(statuses); free(data); delete_grids(sudokuList); } else { // 显式接收0号进程分发的数独网格,匹配tag=0的发送请求 int recv_grid[N*N]; MPI_Recv(recv_grid, N*N, MPI_INT, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); lookForSolution(recv_grid, rank); // 修正MPI_Send参数,传入变量地址而非字面量 int finish_flag = 1; MPI_Send(&finish_flag, 1, MPI_INT, 0, 2, MPI_COMM_WORLD); }
内容的提问来源于stack exchange,提问作者user10823919
相关产品推荐
相关产品推荐

