pthread_create传参异常:添加sleep后恢复正常,是否必须使用sleep?
pthread_create传递参数异常:sleep真的必要吗?
问题描述
我在重复测试pthread_create与pthread_join时发现,传递给线程函数的参数值无法正确传递。但如果在pthread_create后添加sleep()代码,参数就能正常传递。我不清楚其中原因,想请教sleep函数是否真的有必要使用?
测试代码
版本1(无usleep)
#include <unistd.h> #include <stdio.h> #include <pthread.h> void *thread_func(void *arg) { printf("I am thread #%d\n", *(int *)arg); } int main(int argc, char *argv[]) { pthread_t t1[7]; while(1){ for(int i = 0; i < 7; i++){ pthread_create(&t1[i], NULL, &thread_func, &i); } sleep(10); for(int j = 0; j < 7; j++){ pthread_join(t1[j], NULL); } sleep(10); } return 0; }
版本2(添加usleep(1))
int main(int argc, char *argv[]) { pthread_t t1[7]; while(1){ for(int i = 0; i < 7; i++){ pthread_create(&t1[i], NULL, &thread_func, &i); usleep(1); } sleep(10); for(int j = 0; j < 7; j++){ pthread_join(t1[j], NULL); } sleep(10); } return 0; }
原因分析
核心问题不是线程创建的时机,而是你传递给线程的是循环变量i的地址,而非i的当前值。
主线程的for循环执行速度极快,远快于子线程启动并执行printf的速度。当子线程终于开始读取*(int*)arg时,主线程的i早就已经变了——可能已经循环到了下一个值,甚至整个循环结束后i已经等于7了。
添加sleep或usleep只是让主线程暂停,给子线程留出足够时间读取i的当前值,但这是治标不治本的临时方案,完全没必要依赖sleep来解决问题。
正确解决方案
你需要确保每个线程拿到的是独立、不会被主线程修改的参数值,有几种常用方法:
方法1:为每个线程分配独立内存
用malloc给每个线程单独分配int空间,存储当前i的值,线程执行完毕后释放内存:
void *thread_func(void *arg) { int num = *(int *)arg; free(arg); // 释放分配的内存 printf("I am thread #%d\n", num); return NULL; } int main(int argc, char *argv[]) { pthread_t t1[7]; while(1){ for(int i = 0; i < 7; i++){ int *arg = malloc(sizeof(int)); *arg = i; pthread_create(&t1[i], NULL, &thread_func, arg); } sleep(10); for(int j = 0; j < 7; j++){ pthread_join(t1[j], NULL); } sleep(10); } return 0; }
方法2:直接传递值(适用于小范围整数)
如果i的取值范围在指针能容纳的范围内(多数系统中int和指针长度相同),可以直接把i强制转换成void*传递,线程里再转回来:
void *thread_func(void *arg) { int num = (int)arg; printf("I am thread #%d\n", num); return NULL; } int main(int argc, char *argv[]) { pthread_t t1[7]; while(1){ for(int i = 0; i < 7; i++){ pthread_create(&t1[i], NULL, &thread_func, (void*)i); } sleep(10); for(int j = 0; j < 7; j++){ pthread_join(t1[j], NULL); } sleep(10); } return 0; }
方法3:用数组存储独立参数
提前定义一个数组,每个元素存储对应线程的参数值,传递数组元素的地址:
int main(int argc, char *argv[]) { pthread_t t1[7]; int args[7]; // 存储每个线程的参数 while(1){ for(int i = 0; i < 7; i++){ args[i] = i; pthread_create(&t1[i], NULL, &thread_func, &args[i]); } sleep(10); for(int j = 0; j < 7; j++){ pthread_join(t1[j], NULL); } sleep(10); } return 0; }
总结
sleep完全没必要用,它只是掩盖了参数共享导致的竞态问题。解决的核心是让每个线程拿到不会被主线程修改的独立参数,上面几种方法都能从根本上解决问题。
内容的提问来源于stack exchange,提问作者김태정
相关产品推荐
相关产品推荐

