在内存理解上,最著名的例子就是线程启动时的参数传递。
函数启动一个线程,很多时候需要向线程传参数,但是线程是异步启动的,即很可能启动函数已经退出了,而线程函数都还没有正式开始运行,因此,绝不能用启动函数的内部变量给线程传参。道理很简单,函数的内部变量在浮动栈,但函数退出时,浮动栈自动拆除,内存空间已经被释放了。当线程启动时,按照给的参数指针去查询变量,实际上是在读一块无效的内存区域,程序会因此而崩溃。
那怎么办呢?我们应该直接用malloc函数给需要传递的参数分配一块内存区域,将指针传入线程,线程收到后使用,最后线程退出时,free释放。
我们来看例子:
//这个结构体就是参数表 typedef struct _CListen_ListenAcceptTask_Param_ { Linux_Win_SOCKET m_nSocket; //其他参量… … }SCListenAcceptTaskParam; //习惯性写法,设置结构体后,立即声明结构体的尺寸,为后续malloc提供方便 const ULONG SCListenAcceptTaskParamSize = sizeof(SCListenAcceptTaskParam); //这里接收到连接请求,申请参数区域,将关键信息带入参数区域,帮助后续线程工作。 bool CListen::ListenTaskCallback(void* pCallParam,int& nStatus) { //正常的函数逻辑… … //假定s是accept到的socket,需要传入后续线程工作 //在此准备一块参数区域,从远堆上申请 SCListenAcceptTaskParam* pParam = (SCListenAcceptTaskParam*) malloc(SCListenAcceptTaskParamSize); //给参数区域赋值 pParam->m_nSocket = s; //此处启动线程,将pParam传递给线程… … //正常的函数逻辑… … } //这是线程函数,负责处理上文accept到的socket bool CListen::ListenAcceptTask(void* pCallParam,int& nStatus) { //第一句话就是强制指针类型转换,获得外界传入的参数区域 SCListenAcceptTaskParam* pParam= (SCListenAcceptTaskParam*)pCallParam; //正常的函数逻辑… … //退出前,必须要做的工作,确保资源不被泄露 close(pParam->m_nSocket); //关闭socket free(pCallParam); // free传入的参数区域 //… … }