知识点:
在c++的内存模型中,函数内部的变量是保存在栈中,当函数退出时,栈会被释放;而堆不会,需要程序员手动释放;
出现场景:
函数启动一个线程,并向线程传参数;
当启动函数已经退出的时候,线程函数还没有正式开始运行。之后,线程函数开始运行时,按照传入的参数指针去查询变量,实际上是在读一块无效的内存区域,因此程序会崩溃;
案例说明:
#define NUM_THREADS 5
using namespace std;
struct thread_data
{
int thread_id;
char * message;
};
void * PrintHello(void * threadarg) // 线程函数用来输出被传入的参数
{
struct thread_data * my_data;
my_data = (struct thread_data *)threadarg;
cout<< "Thread ID: \t"<<my_data->thread_id;
cout<< "Message: "<<my_data->message<<endl;
pthread_exit(NULL);
return 0;
}
void CallBackFun() // 生成要传入线程的参数
{
pthread_t threads[NUM_THREADS] = {0};
struct thread_data td[NUM_THREADS] = {0};
//struct thread_data *td[NUM_THREADS]; // (1)使用结构体指针数组,方便分配堆内存
int rc;
int i;
for( i=0; i < NUM_THREADS; i++ ){
// td[i] = (struct thread_data *)malloc(sizeof(struct thread_data)); // (2) 给结构体分配堆内存
cout <<"main() : creating thread, " << i << endl;
td[i].thread_id = i;
td[i].message = (char * )"This is message";
rc = pthread_create(&threads[i], NULL,
PrintHello, (void *)&td[i]);
/* td[i]->thread_id = i;
td[i]->message = (char * )"This is message";
rc = pthread_create(&threads[i], NULL,
PrintHello, (void *)td[i]);*/ // (3)
if (rc){
cout << "Error:unable to create thread," << rc << endl;
exit(-1);
}
}
//Sleep(10000);
}
int _tmain(int argc, _TCHAR* argv[])
{
CallBackFun();
getchar();
return 0;
}
上述代码中CallBackFun函数中调用pthread_create创建线程,并生成参数传给启动的线程;PrintHello就是回调函数,输出传入给线程的参数。
直接运行上述代码:
可以看到,发生内存访问错误了;
这里先给出结论:td这个结构体数组是存放在CallBackFun对于的栈中,当CallBackFun函数返回时,td就被释放了,所以这个时候线程函数PrintHello通过指针去访问td中的内容时,相当于是访问了一个无效内存。
解决办法有两种:
(1)将传给线程函数的参数保存在堆中,使用malloc分配内存。注意:后面要记得释放 (2)使用同步机制,让线程函数执行完之后,才能让CallBackFun函数返回。这里我进行了一个简单的测试,就是加了sleep函数;