(Bug收集)C++多线程:线程启动时参数传递引发的内存bug

1,311 阅读2分钟

知识点:

在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函数;