AfxBeginThread函数参数传递的问题
一、背景
最近接手一个软件开发项目,该软件需要同时启动多个线程进行订单作业,而且每个订单可能会处理大批量数据,运行起来时内存占用率有可能会比较大,所以采用多线程作业。同时由于每个线程都会调用某些公用的动态库或全局变量,如果某个动态库不支持多线程调用,如动态库中定义了多个全局变量,则多个线程同时作业时会出现数值错误,所以应该通过函数AfxBeginThread()来进行参数传递,每个线程独自分开处理。
二、AfxBeginThread函数知识
1、AfxBeginThread的原型有两个,一个是用户界面(user-interface)线程,一个是工作者(worker)线程。以下重点介绍工作者线程,其原型如下所示。
CWinThread* AFXAPI AfxBeginThread(
AFX_THREADPROC pfnThreadProc,
LPVOID pParam,
int nPriority,
UINT nStackSize,
DWORD dwCreateFlags,
LPSECURITY_ATTRIBUTES lpSecurityAttrs)
其中:
参数1 :指向工作者线程的控制函数,其值不能为空,声明方式为UINT MyControllingFunc( LPVOID pParam )。
参数2 :传入到控制函数的参数(即pfnThreadProc对应的函数),它的类型为LPVOID。
参数3:指定线程的优先级。
参数4:指定新创建线程的堆栈大小(bytes),如果为0,则默认与创建该线程的线程相同。
参数5:是一个创建标识,如果是CREATE_SUSPENDED,则在悬挂状态创建线程,在线程创建后线程挂起,否则如果该值为0,则线程在创建后立即开始线程的执行。
参数6:指定线程的安全属性,如果该值为NULL,则与创建该线程的线程相同。NT下有用。
返回值:返回新创建的线程指针。
注意:
三、具体实现
对于线程函数中参数的传递,我们这里定义一个结构体类型,用new运算符申请内存并用该指针来传递。
1、函数和结构体全局声明
UINT MyControllingFunction(LPVOID lpParam);
//transfer parameter to thread
typedef struct SthData
{
ungsigned char ucOrderNumber[255];
ungsigned char ucOrderID[255];
}*pSthData;
2、结构体赋值
//pass parameters to thread
pSthData pDataValue = new SthData;
memset(pDataValue, 0x00, sizeof(SthData));
memcpy(pDataValue->ucOrderID, strOrderID.GetBuffer(strOrderID.GetLength()), strOrderID.GetLength());
memcpy(pDataValue->ucOrderNumber,strTask_No.GetBuffer(strTask_No.GetLength()),strTask_No.GetLength());
strOrderID.ReleaseBuffer(-1);
strTask_No.ReleaseBuffer(-1);
//create thread
CWinThread* pThread;
pThread = AfxBeginThread(MyControllingFunction, pDataValue);
delete stcSthData;
stcSthData = NULL;
3、线程内调用
UINT MyControllingFunction(LPVOID lpParam)
{
SthData* stcSthDataData = (SthData *)lpParam;
strOrderID = stcSthDataData->ucOrderID;
strOrderNum = stcSthDataData->ucOrderNumber;
......
......
}
4、结果
点击“运行”按钮后,调试时发现传递进来的参数ucOrderID和ucOrderNumber为乱码,以致于strOrderID和strOrderNum 的值为空。
怎么回事呢?
原来问题就出现在AfxBeginThread(MyControllingFunction, pDataValue)函数后的语句:delete stcSthData。当程序执行到AfxBeginThread(MyControllingFunction, pDataValue)函数这一步时,程序会创建一个进程,并调用MyControllingFunction线程函数,但是同时也会往下执行代码语句,即delete stcSthData。我们知道,用new 运算符可以动态分配内存,而且必须与delete 配对使用。当使用delete运算符时,它会释放撤销刚刚申请的内存地址空间,即会把指向结构体地址所对应的内存释放,导致我们看到的乱码。
5、解决办法
delete stcSthData;
stcSthData = NULL;
应该把以上语句放入到线程函数体内。
UINT MyControllingFunction(LPVOID lpParam)
{
SthData* stcSthDataData = (SthData *)lpParam;
strOrderID = stcSthDataData->ucOrderID;
strOrderNum = stcSthDataData->ucOrderNumber;
delete stcSthData;
stcSthData = NULL; //防止野指针
......
......
}
四、其它
1、线程函数中使用类成员或函数