背景
linux C提供了pthread_create函数用来创建一个子线程,该函数的最后一个参数可以往子线程的函数中传入一个参数,示例如下:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
void *child_thread (void *args)
{
int *argptr = (int *) args;
int arg = *argptr;
printf ("Argument is %d\n", arg);
pthread_exit (NULL);
}
int main(){
int args = 1;
pthread_t tid;
pthread_create(&tid, NULL, child_thread, &args);
pthread_join(tid[i], NULL);
return 0;
}
如果想传入多个参数则可以传入一个结构体变量,在结构体中定义需要用到的参数即可:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
struct MyStruts {
int a;
int b;
};
void *child_thread (void *args)
{
struct MyStruts *argptr = (struct MyStruts *) args;
int a = argptr->a;
int b = arhptr->b;
printf ("Argument is %d %d\n", a, b);
pthread_exit (NULL);
}
int main(){
struct MyStruts args = { 0 };
args.a = 1;
args.b = 2;
pthread_t tid;
pthread_create(&tid, NULL, child_thread, &args);
pthread_join(tid[i], NULL);
return 0;
}
问题说明
上面传入结构体变量的代码看上去似乎没问题,但是其实有隐患:
变量args为局部变量,传入一个局部变量结构体作为子线程的参数,如果子线程在pthread_create()返回之前立即运行没有什么问题,但是不能保证一定会这样。
实际情况是,pthread_create()返回和子线程的执行顺序是随机的。如果pthread_create()先于子线程执行之前返回了,并且父线程所在函数先于子线程返回了,我们在父线程所在函数定义的局部变量就释放掉了,子线程获得的入参的指针可能会指向一个悬空指针,无法正确对参数做操作。
解决方法
为了避免上述情况的发生,我们在往pthread_create函数中传入结构体变量时,一定不能使用局部变量来声明并初始化结构体参数,要使用malloc来动态分配内存,示例如下:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
struct MyStruts {
int a;
int b;
};
void *child_thread (void *args)
{
struct MyStruts *argptr = (struct MyStruts *) args;
int a = argptr->a;
int b = arhptr->b;
printf ("Argument is %d %d\n", a, b);
free(argptr); // 在子线程中释放父线程申请的参数资源,确保参数能准确传入到子线程中使用
pthread_exit (NULL);
}
int main(){
struct MyStruts *args = (struct MyStruts *)malloc(sizeof(struct MyStruts));
args->a = 1;
args->b = 2;
pthread_t tid;
pthread_create(&tid, NULL, child_thread, args);
pthread_join(tid[i], NULL);
return 0;
}
在父线程中申请一块资源,用于存放要传入子线程的结构体变量,然后在子线程使用完入参后再释放这块资源,这样就可以确保入参可以正确被获取到并使用
参考
https://w3.cs.jmu.edu/kirkpams/OpenCSF/Books/csf/html/ThreadArgs.html