VC创建子线程

一.创建子线程调用函数 _beginthread 或 _beginthreadex_beginthreadex声明如下

uintptr_t _beginthreadex( 

   void *security, 

   unsigned stack_size,

   unsigned ( *start_address )( void * ),

   void *arglist,

   unsigned initflag,

   unsigned *thrdaddr 

);

//1个参数:安全属性,NULL为默认安全属性

 //2个参数:指定线程堆栈的大小。如果为0,则线程堆栈大小和创建它的线程的相同。一般用0

 //3个参数:指定线程函数的地址,也就是线程调用执行的函数地址(用函数名称即可,函数名称就表示地址)

 //4个参数:传递给线程的参数的指针,可以通过传入对象的指针,在线程函数中再转化为对应类的指针

 //5个参数:线程初始状态,0:立即运行;CREATE_SUSPENDsuspended(悬挂)

 //6个参数:用于记录线程ID的地址,如果不用的话,可以传NULL0

二.调用如下:

void SSBase::SendSynRequestMethod(ControlSSBean controlSSBean)
{
ControlSSBean * pcontrolSSBeam=new ControlSSBean(controlSSBean);
HANDLE hthread = (HANDLE)_beginthreadex(NULL,0,&SSBase::SendSynRequestMethod_Sub,pcontrolSSBeam,NULL,0);
CloseHandle(hthread);
}
unsigned  int WINAPI SSBase::SendSynRequestMethod_Sub(void * pArugment)
{
ControlSSBean* controlSSBean = (ControlSSBean *)pArugment;
ServerStatusInfo* pServer = ServerStatusHelper::getInstance()->GetServerStatusInfo();
SS_INFO* pInfo = pServer->GetSSInfoSync( controlSSBean->PortfolioID, controlSSBean->StrategyID, controlSSBean->VersionID);
//如果策略是停止的,则退出
if ( !pInfo || pInfo->nStatus != ss_Start )
{
_endthreadex(0);
//CloseSubThreadHandle(controlSSBean.RequestID);
return 0;
}
controlSSBean->Session = pInfo->Session;
char str[60];
itoa((int)controlSSBean->Session, str, 16);
//如果没有注册OutEvent,则需要先注册,如果已经注册了,则直接DealInEvent
string key = controlSSBean->EventID + "_" + str;
if (controlSSBean->EventID !="" && _registedOutEventListCache.find(key) == _registedOutEventListCache.end())
{
_registedOutEventListCache[key] =1;
ControlSSInstance::GetInstance()->SendSynRequestMethod(*controlSSBean);
}
else
{
ControlSSInstance::GetInstance()->DealInEvent(*controlSSBean);
}
_endthreadex(0);
delete controlSSBean;
return 0;
}


三.线程传参问题:

线程开始前申明一个临时变量,然后取它的地址作为参数传递给_beginthread函数,线程里是通过这个地址访问参数的。但是如果你调用_beginthread函数的地方马上函数结束了,这个你分配的临时变量所占用的内存空间可能会被释放掉,所以在线程中访问时会发生越界。 

解决方法: 

你向线程传递参数时,不要申明临时变量然后取它的地址,而是new一个变量,然后将这个地址传递给线程,这个变量在线程中自己使用完毕后释放掉。如果担心处理不方便,可以在线程的入口处申明一个临时变量,让这个临时变量=*p(p为传递进来的参数指针)然后立即将传递进来的指针释放掉。

四.CreateThread_beginthread,_beginthreadex 的区别

1)如果你正在编写C/C++代码,决不应该调用CreateThread。相反,应该使用VisualC++运行期库函数_beginthreadex,退出也应该使用_endthreadex。如果不使用MicrosoftVisualC++编译器,你的编译器供应商有它自己的CreateThread替代函数。不管这个替代函数是什么,你都必须使用。

2)因为_beginthreadex_endthreadexCRT线程函数,所以必须注意编译选项runtimelibaray的选择,使用MTMTD[MultiThreaded , Debug MultiThreaded]

3_beginthreadex函数的参数列表与CreateThread函数的参数列表是相同的,但是参数名和类型并不完全相同。这是因为MicrosoftC/C++运行期库的开发小组认为,C/C++运行期函数不应该对Windows数据类型有任何依赖。_beginthreadex函数也像CreateThread那样,返回新创建的线程的句柄。

下面是关于_beginthreadex的一些要点:

1)每个线程均获得由C/C++运行期库的堆栈分配的自己的tiddata内存结构。(tiddata结构位于Mtdll.h文件中的VisualC++源代码中)。

2)传递给_beginthreadex的线程函数的地址保存在tiddata内存块中。传递给该函数的参数也保存在该数据块中。

3_beginthreadex确实从内部调用CreateThread,因为这是操作系统了解如何创建新线程的唯一方法。

4)当调用CreatetThread时,它被告知通过调用_threadstartex而不是pfnStartAddr来启动执行新线程。还有,传递给线程函数的参数是tiddata结构而不是pvParam的地址。

这个tiddata结构,通过线程本地存储器TLS于线程本身关联起来。我们传入的 线程入口函数就保存在这个结构中。tiddata的作用除了保存线程函数入口地址之外,还有一个重要

的作用就是:C运行时库中有些函数需要通过这个结构来 保存和获取一些数据,比如说errno之类的线程全局变量。当一个线程调用一个要求tiddata结构的运行时库函数的时候,

将发生下面的情况:运行时库函数试图TlsGetv alue获取线程数据块的地址,如果没有获取到,函数就会 现场分配一个 tiddata结构,并且和线程相关联,于是问题出现了,

如果不通过_endthreadex函数来终结线程的话,这个结构将不会被撤销,内存泄漏就会出 现了。但通常情况下,我们都不推荐使用_endthreadex函数来结束线程,因为里面包

含了ExitThread调用。

5)如果一切顺利,就会像CreateThread那样返回线程句柄。如果任何操作失败了,便返回NULL

4_endthreadex的一些要点:

C运行期库的_getptd函数内部调用操作系统的TlsGetValue函数,该函数负责检索调用线程的tiddata内存块的地址。

然后该数据块被释放,而操作系统的ExitThread函数被调用,以便真正撤消该线程。当然,退出代码要正确地设置和传递。

5)虽然也提供了简化版的的_beginthread_endthread,但是可控制性太差,所以一般不使用。

6)线程handle因为是内核对象,所以需要在最后closehandle

 (7) 为什么要用C运行时库的_beginthreadex代替操作系统的CreateThread来创建线程?

来源自自19997MSJ杂志的《Win32 Q&A》栏目

你也许会说我一直用CreateThread来创建线程,一直都工作得好好的,为什么要用_beginthreadex来代替CreateThread,下面让我来告诉你为什么。

回答一个问题可以有两种方式,一种是简单的,一种是复杂的。

如果你不愿意看下面的长篇大论,那我可以告诉你简单的答案:_beginthreadex在内部调用了CreateThread,在调用之前_beginthreadex做了很多的工作,从而使得它比CreateThread更安全

你可能感兴趣的:(VC)