浅谈线程
由于工作中经常跟线程打交道,所以就谈谈自己对线程的一些理解。说到线程就不得不先说进程,进程通常被定义为一个正在运行的程序的实例。进程是一个正在运行的程序,它拥有自己的虚拟地址空间,拥有自己的代码、数据和其他系统资源,如进程创建的文件、管道、同步对象等,一个进程也包含了一个或者多个运行在此进程内的线程。
那进程和程序又有什么区别了?虽然程序和进程在表面上很相似,但是它们有者根本的区别。程序是一连串静态的指令,而进程是一个容器,它包含了一系列运行在这个程序实例上下文中的线程使用的资源。
操作系统通过CreateProcess创建进程或,会创建一个线程执行进行中的的代码(也就是主线程),主线程可以创建其他线程,这些线程叫辅助线程也叫工作线程。
1.线程的创建
线程的创建是通过函数CreateThread来实现的,
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes, //线程的安全属性
DWORD dwStackSize, //线程堆栈的大小
LPTHREAD_START_ROUTINE lpStartAddress, //线程函数的起始地址
LPVOID lpParameter, //传递个线程函数的参数
DWORD dwCreationFlags, //线程创建后是否立即启动
LPDWORD lpThreadId //线程的ID号
);
每个线程都要都要有个入口函数(也就是线程函数),
下面是一个线程函数定义的例子:DWORD WINAPI ThreadProc(LPVOID lpParam);
WINAPI是个宏 #define WINAPI _stdcall;
2.线程的内核对象
线程内核对象就是一个数据结构,包含了线程的一些信息。当成功调用CreateThread后,系统都会在内部为新线程分配一个内核对象。
内核对象的基本成员:
Context
Context也就是线程上下文,包括了一组CPU寄存器,反映了线程上次运行时CPU寄存器的状态。
Usage Count
Usage Count就是线程的使用计数。线程只要没有结束,Usage Count就至少为1。当这个值为0时,系统就认为已经没有任何进程在引用此内核对象了,于是线程内核对象就要从内存中撤销。在创建一个新的线程时,CreateThread函数返回内核对象的句柄,相当于打开一次新创建的内核对象,这会促使Usage Count加1,所以创建一个新的线程后,Usage Count就是2。调用函数OpenThread会使Usage Count再次加1,而CloseHandle会使Usage Count减1。
Suspend Count
Suspend Count暂停次数。用ResumeThread函数可以唤醒一个线程,会减少Suspend Count,当Suspend Count为0时线程被恢复运行。用SuspendThread函数挂起一个线程,这个函数会增加Suspend Count。
Exit Code
Exit Code就是线程退出代码(也就是线程函数的返回值),当线程函数还没有执行完时,Exit Code的值就是STILL_ACTIVE。
Signaled
Signaled指示线程对象是否为”受信”状态。线程运行时Signaled永远是False也就是未受信。只有线程结束后才是受信状态。
3.线程的优先级
首先得说下Windows调度线程的原则:只有优先级最高的线程是可调度的,操作系统就不会将CPU分配给优先级低的。但是有些方法会使线程暂停或者不可调度,如WaitForSingleObject或者GetMessage,这样优先级低的就可以分配到CPU的时间片,一旦优先级高的线程被唤醒了,优先级低的线程将被阻塞。Windows支持6个优先级类:idle、below normal、normal、above normal、high、real-time。
4.线程的终止
终止线程有4种方法:
1. 使用ExitThread函数终止线程
采用这种方法终止线程,会使系统释放掉此线程使用的所有资源但是c/c++资源却不能得到正确的清除。
2. 使用TerminateThread函数在一个线程中强制终止另一个线程的执行
使用TerminateThread函数终止线程,系统不会释放线程使用的堆栈,所以除非迫不得已,尽量避免使用这个函数终止线程。
3. 使用ExitProcess函数结束进程
使用这个方法相当与对进程中的每个线程使用TerminateThread函数。
4. 线程函数自然退出
这种方法是最好的,线程函数返回时Windows将终止线程的执行。
函数说明:
Handle OpenThread(
DWORD dwDesiredAccess,//想要访问的权限
BOOL bInheritHandle, //此函数返回的句柄是否可以被子进程继承
DWORD deThreadId //目标线程的ID
)
Void ExitThread(DWORD dwExitCode);//dwExitCode线程的退出代码
BOOL TerminateThread(HANDLE hThread,DWORD dwExitCode);//hThread目标线程句柄