C多线程问题:循环创建线程时所有线程使用最后一次迭代参数
问题分析与解决方案
问题根源
你在for循环中定义的ThreadArgs myargs是栈上的局部变量,它的内存地址在整个循环过程中是固定的。当调用pthread_create将&myargs传给线程后,线程可能还没来得及执行到ThreadArgs myargs = *args;完成参数拷贝,循环就已经进入下一次迭代,直接覆盖了这个变量里的内容。最终所有线程读取的都是最后一次循环写入的参数值。sleep()能“解决”是因为给了线程足够的时间完成参数拷贝,但这是临时的 workaround,并非正确解法。
解决方案
方案1:动态分配参数内存(推荐,适用于线程数动态的场景)
为每个线程单独分配堆内存存储参数,避免循环覆盖。线程执行完成后记得释放内存,防止泄漏。
修改循环代码:
for(int i = 0; i < NO_OF_THREADS; i++) { int ordered_product = (rand() % NO_OF_PRODUCTS); int ordered_quantity = (rand() % 10) + 1; int customer = (rand() % NO_OF_CUSTOMERS); printf("%d %d %d\n", customer+1, ordered_quantity, ordered_product+1); // 动态分配参数内存 ThreadArgs *myargs = malloc(sizeof(ThreadArgs)); if (myargs == NULL) { perror("malloc failed"); exit(1); } myargs->customer_id = customer; myargs->product_id = ordered_product; myargs->product_quantity = ordered_quantity; int rc = pthread_create(&mythreads[i], NULL, thread_function, myargs); if(rc != 0) { perror("Pthread create"); free(myargs); // 创建失败时释放已分配的内存 exit(1); } }
修改线程函数:
void* thread_function(void* arg) { ThreadArgs* args = (ThreadArgs*) arg; int customer_id = args->customer_id + 1; int product_quantity = args->product_quantity; int product_id = args->product_id + 1; printf("Customer %d purchased %d of Product %d\n", customer_id, product_quantity, product_id); free(args); // 释放动态分配的内存 return NULL; }
方案2:用数组预存所有线程参数(适用于线程数固定的场景)
如果NO_OF_THREADS是编译期确定的常量,可以定义一个数组存储每个线程的参数,每个参数都有独立的内存空间,不会被循环覆盖。
修改代码:
// 循环外定义参数数组,确保内存生命周期覆盖所有线程 ThreadArgs thread_args[NO_OF_THREADS]; for(int i = 0; i < NO_OF_THREADS; i++) { int ordered_product = (rand() % NO_OF_PRODUCTS); int ordered_quantity = (rand() % 10) + 1; int customer = (rand() % NO_OF_CUSTOMERS); printf("%d %d %d\n", customer+1, ordered_quantity, ordered_product+1); // 填充当前线程的参数 thread_args[i].customer_id = customer; thread_args[i].product_id = ordered_product; thread_args[i].product_quantity = ordered_quantity; int rc = pthread_create(&mythreads[i], NULL, thread_function, &thread_args[i]); if(rc != 0) { perror("Pthread create"); exit(1); } }
线程函数可以保持你的原始实现,因为数组元素的内存不会被循环覆盖,线程可以安全拷贝参数。
方案3:循环内等待线程完成(串行执行,仅适用于调试或低并发场景)
这种方法会让线程逐个执行,失去多线程并行的优势,但实现最简单:
for(int i = 0; i < NO_OF_THREADS; i++) { // ... 生成参数、创建线程的代码 ... pthread_join(mythreads[i], NULL); // 等待当前线程执行完毕再进入下一次循环 }
内容的提问来源于stack exchange,提问作者MoChahadeh
相关产品推荐
相关产品推荐

