线程:
线程从启动到消亡过程详细讲解:
1:从CreateThread开始,会创建一个线程内核对象(是一个结构体),线程内核对象里面有:
使用计数(CreateThread后为2),用于记录。
暂停计数,暂停计数为UINT类型,为0才可以运行,在刚初始化线程内核对象后,其值为1。
退出代码(STILL_ACTIVE):
Signaled(FALSE):
2:创建内核对象后会分配一块栈空间,会在前面的位置压入一些参数:lpParam、lpfnAddr。
3:每个线程都有一个线程上下文的结构体(CONTEXT),在WinNT.h里面:
线程上下文里面保存了上一次运行的寄存器状态。特别关心的是EIP和ESP寄存器的值,EIP为指令寄存器,指向下一条代码在哪里(RtlUserThreadStart),ESP为栈寄存器,指向栈顶(lpfnAddr)。RtlUserThreadStart这个函数是不可以调用的,他是一个未导出函数,但是其是存在的,接收两个参数:lpParam和lpfnAddr,返回值为void。线程切换的时候,完全是靠线程上下文来做的。每次切换就会将状态加载到CPU。
4:交给CPU调度,运行。
5:最后,RtlUserThreadStart会设置一个结构化异常(SEH),可以让操作系统处理这些异常情况,然后会调用线程函数,并且将lpParam参数传递进去,他会等待线程函数的返回,线程返回之后,会在内部调用ExitThread,并且将线程函数的返回值传递给ExitThread,然后线程内核对象的使用计数就会减少,线程消亡。
_beginthreadex和CreateThread
1:errno是C语言的错误处理方法,但是其是非线程安全的,他们本身是通过全局变量来做的。_beginthreadex里面传递的参数意义与CreateThread是一样的但是意义有区别,前者是C语言的,里面的变量类型都是C语言标准的,后者是C++的,参数类型使用了Windows的一些变量类型。
2:_beginthreadex会多分配一块空间来保存errno这一类全局变量,他不会被其他线程更改,来确保线程安全。然后会调用CreateThread来创建线程。左后需要调用_endthread来释放,他会将多分配的那一块控件释放掉。
3:以后创建线程最好使用_beginthreadex,而不实用CreateThread,他会相对更加安全。_beginthread也不会多分配这一块空间,与_beginthreadex是有区别的。