目录
线程概述
进程与线程的关系
线程调度原理
单核与多核的线程处理方式
多线程
线程相关API
线程分配
线程切换时
线程状态
线程退出
线程退出时做的事(正常退出情况)
线程概述
程序 | 磁盘上的一个可执行文件(由指令和数据等组成的二进制文件) |
---|---|
进程 | 程序执行代码所需的资源的集合,线程的容器,包括虚拟地址空间、代码、数据、对象句柄、环境变量等。特性:不活泼,懒惰的(不执行代码) |
线程 | 程序执行代码的最小单位。负责执行进程地址空间中的代码。特性:活泼,勤奋。 |
组成 | 线程内核对象。操作系统用它来管理线程,存放线程统计信息。 |
线程堆栈。用于维护线程在执行时,需要的所有函数参数和局部变量。 | |
时间片 | 操作系统为每一个运行线程安排一定的CPU时间 。 |
PS: | 每个进程exe启动时系统会创建一个主线程。进程可以有多个线程。(个数最好为CPU核心数 * 2) |
进程与线程的关系
- “不活泼”:进程不执行代码,是线程的容器。
- 若要使进程完成某项操作,则必须是它拥有的线程负责执行包含在进程地址空间中的代码。
- 数量关系:每个进程至少拥有一个线程。
- 进程建立时,操作系统会自动创建这个进程的第一个线程,称为主线程。此后,该线程可以创建其他的线程。
- 单个进程可能包含若干个线程,这些线程都共享进程的地址空间、对象句柄、代码和数据等其他资源。
- 生命周期:
- 进程中所有线程均退出时,进程退出,系统撤销进程拥有的地址空间和其他资源。
线程调度原理
- CPU某个时刻只执行一行代码,每个线程切片执行 。
- CPU的时间切片:CPU把执行代码的时间切分成不同的比较短的时间。
- 调度原理:每个线程分配一个时间切片,线程执行完后切换下一个时间片,直到全部线程执行完。
- 线程执行完当前时间切片后,要保存环境。下次继续执行的时候还原环境,不会从头执行。
- 所有线程都获得时间切片,线程开的越多,获得的CPU时间切片越多。
- 线程数量和效率有个临界点,线程开的越多,线程等待的时间也越长。
- CPU支持超线程,线程数最好不超过CPU核心数 * 2,不支持则线程数 = 核心数。
- 线程优先级不用设置,调用后效果不大。系统内部有一套算法,越繁重的任务优先级越高。
操作系统为每个线程保存各自的寄存器和栈环境,共享进程的地址空间、对象句柄、代码和数据等其他资源,可以执行相同的代码,可以对相同的和数据进行操作。
单核与多核的线程处理方式
对于单核处理器,不同的线程实际上是在轮流使用同一个处理器,一个程序的运行速度不会因为创建了多个线程而加快,因为线程多了以后,每个线程等待时间片的时间也就越长。
对于多核处理器,操作系统可以将不同的线程安排到不同的处理器内核中执行,系统可以同时执行与计算机上的CPU处理器内核一样多的线程,这样一个进程中的多个线程会因为同时获得多个时间片而加快整个进程的运行速度。
多线程
多线程API使用:Winodws核心编程 多线程-CSDN博客
线程相关API
CreateThread/_beginthreadex | 创建线程 |
---|---|
ExitThread/_endthreadex | 退出线程 |
SuspendThread | 挂起线程 |
ResumeThread | 恢复线程 |
TerminateThread | 终止线程 |
GetCurrentThread | 获取当前线程句柄(伪) |
GetCurrentThreadId | 获取当前线程ID |
GetExitCodeThread | 获取线程退出码 |
线程分配
线程通常分为两大类,他们负责不同的工作。 | ||
---|---|---|
UI线程(主线程) | 负责消息处理等。 | 规则:不应该处理1/10秒以上的工作 |
非UI线程(工作线程) | 负责干活 | 不会引起消息阻塞的情况,就算有死循环,也是其他线程,和主线程没有关系 |
线程切换时
- 保存及恢复环境:
- –某线程时间片开始:系统将线程的寄存器值恢复并开始执行。
- –某线程时间片结束:系统将线程的寄存器值保存。
- 不同进程的线程切换:
- –操作系统同时切换物理内存到线性地址空间(虚拟内存)的映射关系,这样线程取的就是自己所属的进程中的代码和数据。
线程状态
线程的状态包括以下几种:
-
就绪状态(Ready):当线程被创建,且已经准备好执行时,处于就绪状态。线程在就绪状态时,等待系统调度器将其分配给一个可用的处理器。
-
运行状态(Running):当就绪状态的线程被系统调度器选择,并分配给一个处理器时,线程进入运行状态,开始执行线程的代码。
-
阻塞状态(Blocked):当线程被阻塞,无法继续执行时,进入阻塞状态。线程可能因为等待某个事件的发生(如I/O操作、锁资源的获取等)或者被其他高优先级任务抢占而进入阻塞状态。
-
挂起状态(Suspended):线程在运行状态或就绪状态时,可能被挂起(Suspend),即暂停执行。挂起状态的线程不会被调度执行。线程可以通过恢复(Resume)操作回到就绪或运行状态。
-
终止状态(Terminated):当线程的代码执行完成或者出现异常,线程进入终止状态。终止状态的线程不再被调度执行,线程的资源被释放。
线程退出
线程退出的办法:
- 线程函数的返回(推荐用法)
- ExitThread函数(不推荐)
- 促使系统清除该线程所使用的所有操作系统资源,但是 C++对象等资源将不被撤销(比如new变量)
- TerminateThread函数( 应该避免这种方法)。同一个进程或者另一个进程中的线程调用
- 是异步运行函数,它告诉系统希望线程终止,但不保证线程撤销的操作结果。
- –拥有线程的进程终止运行之前,系统不撤销线程堆栈。
- –DLL接不到通知。
- 包含线程的进程终止( 应该避免使用这种方法)
自然死亡
DWORD WINAPI CountThreadProc(LPVOID lpParameter)
{
HWND hDlg = (HWND)lpParameter;
while (g_bRun)
{
//增加计数值
++g_nCount;
//Sleep(1000);
//显示到界面
SetDlgItemInt(hDlg, EDT_SHOW, g_nCount, TRUE);
}
return 0;
}
ExitThread
TerminateThread
线程退出可以通过自然退出、调用ExitThread函数和调用TerminateThread函数来实现。下面是对比它们的说明:
-
自然退出(Natural Exit):线程在执行完自己的任务后,正常地从线程函数中返回,这是线程的一种正常退出方式。线程在自然退出时会依次执行一些清理工作(如释放资源、关闭文件等),并将返回值传递给创建线程的代码。
-
调用ExitThread函数:ExitThread函数是线程库提供的一个函数,用于显式地终止当前线程的执行。调用ExitThread函数会立即终止当前线程,并且不会执行线程的清理工作,包括不会触发析构函数的调用。此外,ExitThread函数会将指定的退出码传递给创建线程的代码。
-
调用TerminateThread函数:TerminateThread函数是操作系统提供的一个函数,用于强制终止指定线程的执行。调用TerminateThread函数会立即终止指定线程,但是它不会给线程执行清理工作的机会,包括不会触发析构函数的调用。此外,TerminateThread函数还可能会导致资源泄漏或其他不可预料的后果,因此在一般情况下,不建议使用TerminateThread函数。
线程退出时做的事(正常退出情况)
- 线程使用的堆栈等被释放。
- 系统将线程对象的退出代码设置为线程的退出码。
- 线程内核对象使用计数 -1.
- 线程内核对象状态变为已通知。