C++创建新线程的四种方式:
方式一(不推荐):CreateThread(记得关闭线程句柄) CreateThread是一种微软在Windows API中提供了建立新的线程的函数,该函数在主线程的基础上创建一个新线程。线程终止运行后,线程对象仍然在系统中,必须通过CloseHandle函数来关闭该线程对象。
HANDLE WINAPI CreateThread( LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, lpParameter, DWORD dwCreationFlags, lpThreadId );
// 线程安全属性,在Windows NT中,NULL使用默认安全性,不可以被子线程继承,否则需要定义一个结构体将它的bInheritHandle成员初始化为TRUE。因此一般使用NULL
SIZE_T dwStackSize, // 线程堆栈大小 设置初始栈的大小,以字节为单位,如果为0,那么默认将使用与调用该函数的线程相同的栈空间大小。任何情况下,Windows根据需要动态延长堆栈的大小
LPTHREAD_START_ROUTINE lpStartAddress, // 线程函数地址 指向线程函数的指针,形式:@函数名,函数名称没有限制, LPVOID lpParameter, // 线程函数参数 是一个指向结构的指针,不需传递参数时,为NULL
DWORD dwCreationFlags, // 指定线程是否立即启动 一般取值为0(立即激活)或者为)CREATE_SUSPENDED(0x00000004)挂起线程 LPDWORD lpThreadId // 存储线程ID号 );
返回值:函数成功,返回线程句柄;函数失败返回false。若不想返回线程ID,设置值为NULL。
函数声明方式 DWORD WINAPI 函数名 (LPVOID lpParam); //标准格式
DWORD WINAPI 函数名 (LPVOID lpParam) { return 0; } CreateThread(NULL, 0, 函数名, NULL, 0, NULL); 1 2 3 4 5 void 函数名(); 也就是普通的没有标准的函数声明方式
需要注意:使用void 函数名()此种线程声明方式时,lpStartAddress需要加入LPTHREAD_START_ROUTINE转换,也就是创建线程时第三个参数要写成(LPTHREAD_START_ROUTINE)函数名
创建方式的缺点:使用这种方法可能会引发内存泄漏问题,不推荐,推荐使用方式三
方式二(简单):_beginthread()
unsigned long _beginthread( void(_cdecl *start_address)(void *), //声明为void (*start_address) (void *)形式 unsigned stack_size, //是线程堆栈大小,一般默认为0 void *arglist //向线程传递的参数,一般为结构体 );
_Beginthread函数创建一个在start_address开始执行例程的线程。 当线程从该例程返回时,就会自动终止。使用此函数必须包含头文件 #include <process.h>
这个函数看起来过于简单,他无法做到线程的挂起状态。另外重要的一点是,它所产生出来的线程所做的第一件事情就是关闭掉handle,所以由它所返回的handle可能可用,也可能不可用
方式三(推荐,安全度高):_beginthreadex(需要关闭线程句柄) uintptr_t _beginthreadex( // NATIVE CODE void *security, unsigned stack_size, unsigned ( __stdcall *start_address )( void * ), void *arglist, unsigned initflag, unsigned *thrdaddr ); 1 2 3 4 5 6 7 8 需要使用头文件#include <process.h> 需要的设置:ProjectàSetting–>C/C+±->User run-time library 选择Debug Multithreaded 或者Multithreaded。即使用: MT或MTD。退出时需要使用_endthreadex(handle);
方式四:AfxBeginThread(会自动释放的,不用你去释放) AfxBeginThread一个计算机函数,功能是创建用户界面线程和工作者线程。 常见用法是AfxBeginThread(ThreadProc,this)。比较常见的就是在MFC里面使用。两者的区别在于用户页面能够处理消息响应,而后者工作者线程则不能。
主要介绍工作者线程:
CWinThread* AFXAPI AfxBeginThread( AFX_THREADPROC pfnThreadProc,// 线程的入口函数,声明一定要如下: UINT MyThreadFunction( LPVOID pParam );
LPVOID pParam,//传递入线程的参数,注意它的类型为:LPVOID,所以我们可以传递一个结构体入线程.
int nPriority,//优先级
UINT nStackSize,//堆栈大小 DWORD
dwCreateFlags,//创建标识,是否挂起 LPSECURITY_ATTRIBUTES lpSecurityAttrs//安全属性 ) 一个是线程函数的指针,一个是传递给这个函数的参数。
实际中我们经常这样用 AfxBeginThread(ThreadProc,this);//把this传过去,就可以调用类的成员了. 这样线程函数就可以使用和操作类的成员了。千万要注意线程函数是静态类函数成员。当然也可以把this换成别的结构体指针,效果一样。
需要注意的是,声明必须是这种方式
UINT MyThreadProc( LPVOID pParam ) { return 0; }