Masimaro's blog - 线程
type: channel
link: https://www.masimaro.top/tag/%E7%BA%BF%E7%A8%8B/
items: windows 线程

windows 线程
type: item
date: 2016-09-14T20:57:00+08:00
description: 在windows中进程只是一个容器，用于装载系统资源，它并不执行代码，它是系统资源分配的最小单元，而在进程中执行代码的是线程，线程是轻量级的进程，是代码执行的最小单位。从系统的内核角度看，进程是一个内核对象，内核用这个对象来存储一些关于线程的信息，比如当前线程环境等等，从编程的角度看，线程就是一堆寄存器状态以及线程栈的一个结构体对象，本质上可以理解为一个函数调用，一般线程有一个代码的起始地址，系统需要执行线程，只需要将寄存器EIP指向这个代码的地址，那么CPU接下来就会自动的去执行这个线程，线程切换时也是修改EIP的值，那么CPU就回去执行另外的代码了。什么时候不用多线程？一般我们都讨论什么时候需要使用多线程，很多人认为只要计算任务能拆分成多个不想关的部分，就可以使用多线程来提升效率，但是需要知道的是，线程是很耗费资源的，同时CPU在线程之间切换也会消耗大量的时间，所以在使用上并不是多线程就是好的，需要慎重使用，在下列情况下不应该使用多线程：当一个计算任务是严重串行化的，也就是一个计算步骤严重依赖上一个计算步骤的时候。但是如果是针对不同的初始参数可以得到不同的结果那么可以考虑用多线程的方式，将每个传入参数当作一个线程，一次计算出多组数据。当多个任务有严格的先后逻辑关系的时候，这种情况下利用多线程需要额外考虑线程之间执行先后顺序的问题，实际上可能它的效率与普通的单线程程序差不多，它还需要额外考虑并发控制，这将得不偿失当一个服务器需要处理多个客户端连接的时候，优先考虑的是使用线程池，而不是简单的使用多线程，为每个客户端连接创建一个线程，这样会严重浪费资源，而且线程过的，CPU在进程调度时需要花大量的时间来执行轮询算法，会降低效率。主线程进程的入口函数就是主线程的入口函数，一般主线程推出进程退出（这是由于VC++中在主线程结束时会隐含的调用ExitProcess）线程入口地址在windows中需要为线程指定一个执行代码的开始地址，这个在VC++中体现为需要为每个进程制定一个函数指针，并制定一个void* 型的指针类型的参数。当CPU执行这个线程时会将寄存器环境改为这个线程指定的环境，然后执行代码（具体就是前面所说的更改EIP的值）创建线程在windows中创建线程所使用的API是CreateThread，这个函数的原型如下:HANDLE CreateThread( LPSECURITY_ATTRIBUTES lpsa, DWORD cbStack, LPTHREAD_START_ROUTINE lpStartAddr, LPVOID lpvThreadParam, DWORD fdwCreate, LPDWORD lpIDThread );第一个参数是线程对象的安全描述符第二个参数是线程栈的大小，每个线程都有一个栈环境用来存储局部变量，以及调用函数，这个值可以给0，这个时候系统会根据线程中调用函数情况动态的增长，但是如果需要很大的线程栈，比如要进行深层的递归，并且每个递归都有大量的局部变量和函数时，运行一段时间后，可能会出现没有足够大的地址空间再来增长线程栈，这个时候就会出现栈溢出的异常，为了解决这个问题，我们可以填入一个较大的值，以通知系统预留足够大的地址空间。第三个参数是线程的入口函数地址，这个是一个函数指针，函数的原型是DWORD ThreadProc( LPVOID lpParameter); 函数中就是线程将要执行的代码。第四个参数是函数中将要传入的参数，为了方便传入多个参数，一般将要使用的过个参数定义为一个结构体，将这个结构体指针传入，然后再函数中将指针转化为需要的结构体指针，这样就可以使用多个参数。第五个参数是创建标志，默认一般传入0，但表示线程一被创建马上执行，如果传入CREATE_SUSPENDED,则表示线程先创建不执行，需要使用函数ResumeThread唤醒线程，另外在XP以上的系统中可以使用STACK_SIZE_PARAM_IS_A_RESERVATION结合上面的第二个参数，表示当前并不需要这么内存，只是先保留当前的虚拟地址空间，在需要时有程序员手动提交物理页面。如果没有指定那么回默认提交物理页面。第六个参数会返回一个线程的ID。下面是一个创建线程的例子:typedef struct tag_THREAD_PARAM { int nThreadNo; int nValue; }THREAD_PARAM, *LPTHREAN_PARAM; int _tmain(int argc, TCHAR *argv[]) { LPTHREAN_PARAM lpValues = (LPTHREAN_PARAM)HeapAlloc(GetProcessHeap(), 0, 10 * sizeof(THREAD_PARAM)…