多线程程序异常:前若干线程被跳过,最后一线程处理多数任务
多线程任务拆分异常:前序线程被跳过,最后一线程处理大部分任务
我花了好几个小时都没找出问题所在。我写了一个从0到9计数的程序(目标是实现1到10,可调整数组填充逻辑),预期用多线程实现,并根据指定线程数动态拆分任务数组。但实际运行出现异常:前2个线程直接被跳过,最后一个线程处理了大部分任务。我怀疑是创建线程的for循环出了问题,调整过第57行的循环索引,虽有局部变化,但核心问题仍未解决。
程序代码
#include <stdlib.h> #include <stdio.h> #include <pthread.h> typedef struct { int *array; int batch; int start; int end; } Parameter; void *method(void *p) { Parameter *param = (Parameter *)p; for (int i = param->start; i < param->end; i++) { printf("Start:%d\tEnd:%d\tIndex:%d\tValue:%d\n", param->start, param->end, i,param->array[i]); } } int main(int argc, char **argv) { // 获取用户输入 int array_length = atoi(argv[1]); int batches = atoi(argv[2]); printf("User specified Array:%d\tBatch:%d\n", array_length, batches); // 创建数组 int *array = (int *)calloc(array_length, sizeof(int)); // 填充数据 for (int i = 0; i < array_length; i++) { array[i] = i; } // 确定批处理大小 int batch_size = array_length / batches; int remainder = array_length % batches; printf("%d\n", batch_size); printf("%d\n", remainder); int start = 0; int end = 0; int index =0; // 参数列表 Parameter *param = (Parameter *)calloc(batches, sizeof(Parameter)); pthread_t *threads = (pthread_t *)calloc(batches, sizeof(pthread_t)); // 遍历每个批处理 for (int i = 0; i < batches; i++) { printf("\n\nBatch number -> %d\n", i); end = start + batch_size; if (remainder > 0) { remainder --; end ++; } // 填充参数 param[i].array = array; param[i].end = end; param[i].start = start; param[i].batch = i; // 创建线程 pthread_create(threads + index, NULL, method, (void *)¶m[i]); index++; start = end; } for (int i = 0; i < batches; i++) { pthread_join(threads[i], NULL); } free(param); free(threads); free(array); return 0; }
问题分析
核心问题是线程参数的竞态条件:
- 你在
pthread_create中传递的是¶m[i],也就是param数组中当前元素的地址。但主线程的for循环会快速执行,在新线程还未读取参数内容前,就已经修改了param[i]的后续元素(甚至当i递增后,循环会覆盖下一个param元素)。 - 当线程真正开始执行时,读取到的
param内容已经被主线程的后续循环修改,导致多个线程拿到了最后一个线程的参数,出现前序线程被“跳过”、最后一线程处理大量任务的现象。
额外的小问题:
- 线程函数
method没有返回值,不符合pthread要求的void*(*)(void*)函数签名,需补充return NULL;。 - 数组填充逻辑是从0开始,若目标是1到10,可改为
array[i] = i+1;。
修复方案
方案:为每个线程分配独立参数内存
不再复用param数组的元素,而是为每个线程单独分配Parameter结构体内存,确保主线程的循环不会干扰线程已获取的参数:
修改主线程创建线程的代码段
// 遍历每个批处理 for (int i = 0; i < batches; i++) { printf("\n\nBatch number -> %d\n", i); end = start + batch_size; if (remainder > 0) { remainder --; end ++; } // 为当前线程分配独立的参数内存 Parameter *thread_param = (Parameter *)malloc(sizeof(Parameter)); thread_param->array = array; thread_param->end = end; thread_param->start = start; thread_param->batch = i; // 创建线程,直接使用i作为threads数组索引,无需额外index变量 pthread_create(&threads[i], NULL, method, (void *)thread_param); start = end; }
修改线程函数,释放参数内存
void *method(void *p) { Parameter *param = (Parameter *)p; for (int i = param->start; i < param->end; i++) { printf("Start:%d\tEnd:%d\tIndex:%d\tValue:%d\n", param->start, param->end, i,param->array[i]); } // 释放为当前线程分配的参数内存 free(param); // 补充符合要求的返回值 return NULL; }
额外优化
原代码中的index变量完全多余,直接用循环变量i作为threads数组的索引即可,简化逻辑。
验证效果
运行./your_program 10 3时,三个线程会正确拆分任务:
- 线程0:处理索引0-4(共5个元素,因为余数1分配给第一个线程)
- 线程1:处理索引4-7(共3个元素)
- 线程2:处理索引7-10(共3个元素)
每个线程都会输出对应区间的内容,不会出现前序线程被跳过的异常。
内容的提问来源于stack exchange,提问作者Patrick Frendo
相关产品推荐
相关产品推荐

