标签:
6.1 线程基础
(1)线程组成:线程内核对象+线程栈(注意:进程=进程内核对象+地址空间)
①从内核角度看,线程是一个内核对象,系统用它来存储一些关于线程的统计信息(比如运行时间等)
②从编程角度看,线程是一堆寄存器状态以及线程栈的一个结构体对象。本质上可以理解为一个函数的调用器(其中的寄存器状态用于控制CPU执行,栈用于存储局部变量和函数参数及函数的返回地址)——为什么要使用线程栈的?
线程1 |
线程2 |
备注(使用线程栈的原因分析) |
void func1(){ int a; int b; } |
void func2(){ int c; int d; } |
如果不为每个线程分配线程栈,而使用进程中某一共同的栈,设func3先于func4执行,则变量进栈顺序a、b,如果此时执行线程2,则c、d也会进栈,栈顶指针指向d。假设这时func3执行完,要回收栈则会出现将c、d弹出栈的错误。现实中可能会出现更复杂的情况。当然,如果这两个线程严格串行执行,则不会出现这种错误。 |
③线程还可以带有消息队列(GUI线程内部会创建)和APC队列。(但注意这些队列在线程创建时并不同时创建,要在调用GUI函数里才会被创建!)
★进程是线程的容器,线程共享进程的地址空间和资源
(2)什么时候不使用多线程
①当一个算法本身是严格串行化的时候,即计算的每一步都严重依赖前一个操作步骤的结果时,不适合用多线程)。
②当多个功能任务具有比较严格的先后逻辑关系时,不宜采用多线程。因为这涉及到线程同步方法的严格控制,从而可能因加了过多的同步而降低了效率。
③还有一种特殊情况,比如一个服务器需要处理成千上万个客户端连接,不宜使用多线程,因为过多的线程间的切换也会降低效率,这里可以考虑用线程池。
6.2 主线程
(1)进程的入口函数,从本质上看就是主线程的入口函数。在C\C++下是WinMainCRTStartup
(2)主线程是进程内第1个可执行的线程实体,它可以用来创建别的线程。
(3)主线程退出后,进程也会退出(因为VS嵌入的入口函数会调用ExitProcess终止其它线程的执行。(当自定义入口时,这个行为就要在自定义的入口函数中自行的维护,即自定义入口函数时,那么进程将在最后一个线程退出后,才退出。因此,主线程也未必是最后一个线程!)。
6.3 线程函数(也叫线程入口函数)
(1)线程函数的原型:DWORD WINAPI ThreadProc(LPVOID lpParameter);
(2)线程函数是线程执行的起点,可以执行我们希望的任何任务
(3)当线程函数执行完毕,线程将退出,同进线程栈也会被释放,线程内核对象的使用计数递减,如果计数为0,则删除该线程内核对象。(可见线程内核对象的生命期可能长于线程本身!)
(4)线程函数必须有一个返回值,它会成为该线程的退出代码。其他线程可以用GetExitCodeThread来检查线程是否己终止运行,并进一步判断其退出代码。
(5)线程函数应尽可能使用函数参数和局部变量。因为静态变量或全局变量,多线程时可能因同时访问这些变量而要进行额外的同步。由函数参数和局部变量是在线程栈上创建的,不会出现多线程同时访问的问题。
6.4 CreateThread函数
参数 |
描述 |
psa |
指向一个SECURITY_ATTRIBUTES结构体。使用默认安全属性时传入NULL |
cbStackSize |
①用于指定线程初始时的栈大小,通常传入0即可,此时系统会使用一个合适的大小。默认是1MB(保存在PE文件中!) ②线程栈溢出时,产生异常,这可以用来捕获代码中无穷递归bug。若没限制耗尽进程所有的地址空间。 |
pfnStartAddr |
新线程入口函数的地址(注意:新线程和调用CreateThread函数的线程可以同时被执行,这是windows抢占式的特点) |
pvParam |
传给线程入口函数的参数,可以是一个数值或一个结构体 |
dwCreateFlags |
0——创建后立即执行;CREATE_SUSPENDED——创建后挂起,并不执行 |
pdwThreadId |
得到新线程ID |
返回值 |
成功——线程内核对象的句柄;失败——NULL |
6.5 终止运行线程
(1)4种终止线程的方式
终止方式 |
描述 |
线程函数返回 |
强烈推荐 ,这是保证所有资源被正确清理的唯一方式!可以确保以下工作正确执行。 ①该函数中的所有C++对象被正确析构。②正确释放线程栈;③把线程退出代码设为函数的返回值;④递减内核对象的计数。 |
ExitThread |
①“杀死主调线程”,操作系统将清理该线程使用的所有操作系统资源(包括线程堆栈) ②可以指定dwExitCode为线程的退出代码;③C\C++资源不会被销毁 |
TerminateThread |
①杀死任何线程;②线程内核对象减1;③不销毁线程堆栈,微软故意这样做,是为了保证其他线程还可以访问被“杀死”线程栈上的值,该堆栈会等到进程结束时才被释放。③该函数是异步的,函数返回时并不保证另一线程被终止。可用WaitForSingleObject判断线程是否终止。 ④将不会通知DLLMain函数某个线程退出,可能导致资源无法释放。 |
进程终止运行时 |
①ExitProcess或TerminateProcess会终止进程中所有进程,同时释放资源。 ②这两个函数就好象为每个线程调用TerminateThread,所以C++对象的析构不会被调用,数据不会回写磁盘…… |
(2)线程终止运行时
①线程拥有的所有用户对象句柄被释放(如窗口和钩子句柄)
②线程退出代码从STILL_ACTIVE变成传给ExitThread或TerminateThread参数的退出代码。
③线程内核对象的状态变为触发状态,线程内核对象的使用计数减1。
④如果线程是进程的最后一个活动线程,则进程也被终止。
标签:
原文地址:http://www.cnblogs.com/5iedu/p/4697161.html