四种方式创建c++线程
线程和进程概念关系:
进程是一组离散的(执行)程序任务集合;
线程是进程上下文中执行的代码序列;
两者之间具体关系:
线程是进程的可执行单元,是计算机分配CPU机时的基本单元。一个进程可以包含一个或多个线程,进程是通过线程去执行代码的。同一个进程的多个线程共享该进程的资源和操作系统分配给该进程的内存空间。每个进程必须有一个主线程,主线程退出之后该进程也就退出了。一个进程的主线程是由系统创建的。
在单CPU中,表面上看好像是多个进程中的多个线程共同执行,实际上是操作系统根据调度规则、依次的 将一个一个的线程可执行代码加载进CPU中执行的;即,CPU在同一时刻只能执行一段代码,由于CPU的频率非常快,迅速的在各个线程间进行切换,所以给人的感觉就好像是多个线程共同执行。
在多核CPU的电脑中,确实是多个线程共同执行,因为多核处理器中,每个核拥有自己的缓存、寄存器和运算器。
C++创建新线程的四种方式:
方式一(不推荐):CreateThread(记得关闭线程句柄)
CreateThread是一种微软在Windows API中提供了建立新的线程的函数,该函数在主线程的基础上创建一个新线程。线程终止运行后,线程对象仍然在系统中,必须通过CloseHandle函数来关闭该线程对象。
HANDLE WINAPI CreateThread( LPSECURITY_ATTRIBUTES lpThreadAttributes, // 线程安全属性,在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);
void 函数名(); 也就是普通的没有标准的函数声明方式
需要注意:使用void 函数名()此种线程声明方式时,lpStartAddress需要加入LPTHREAD_START_ROUTINE转换,也就是创建线程时第三个参数要写成(LPTHREAD_START_ROUTINE)函数名
创建方式的缺点:使用这种方法可能会引发内存泄漏问题,不推荐,推荐使用方式三
Create Thread参考:
百度百科:https://baike.baidu.com/item/CreateThread
方式二(简单):_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 );
需要使用头文件#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; }