- 程序(.EXE文件):磁盘上的可执行文件,属性是文件
- 进程:可执行文件在内存中的映射,进程本身无法执行代码,需要交由线程执行,进程可以概括为可执行文件执行所需要的资源总和:
- 4G虚拟内存
- 可执行文件映射到内存
- 加载的的DLL模块
- 环境变量
- 句柄表
- 进程的内核对象
- 线程:操作系统分配cpu时间的最小单位。
- 进程启动时,操作系统会自动创建这个进程的第一个线程(主线程),该线程可以创建其他的线程。
- 合适的线程数量: cpu的核数 * 2
- 线程有自己的独立的堆栈,同一进程的线程共享进程所拥有的全部资源
- 线程切换
- 线程时间片开始:系统将线程的寄存器值恢复并开始执行。
- 线程时间片结束:系统将线程的寄存器值保存。
- 操作系统:分任务型和时间型两种
- 任务型(dos):只能等待一个任务执行完成后执行下一个任务
- 时间型(windows)
- 操作系统为每一个运行线程安排一定的CPU时间
- 操作系统为每个线程轮流分配时间片,给用户的感觉,就好像线程是同时运行的一样。
- 线程划分
- 带界面的线程:用于创建窗口,建立消息循环,派发消息, 一般都是主线程
- 不带界面的线程:不处理消息,用来处理长时间需要解决的粗活,后台线程
- 1/10 秒原则 -- 在界面线程中,一般不要处理任务超过1/10秒
- 创建线程(
CreateThread
)- 系统自动创建内核对象{CONTEXT(线程上下文)}
- 系统自创建线程堆栈
- 加载dll,
dllmain
被调用DLL_THREAD_ATTACH
参数传入
创建线程语法
int main()
{
char szBuf[100] = "abcde";
DWORD dwThreadId = 0;
hTread = CreateThread(
NULL, //安全属性
0, //使用默认栈大小,一般是1M,在链接选项(堆栈保留大小)可以修改
ThreadProc, //线程回调函数
(LPVOID)szBuf, //自定义参数
0, //线程是否立即运行
&dwThreadId); //传出参数:线程ID
if (hTread == NULL)
{
cout << "线程创建失败" << endl;
}
while (1)
{
Sleep(1000);
ResumeThread(hTread);//恢复线程,此时线程内的代码根据上次保存的进程上下文,继续执行
}
system("pause");
return 0;
}
线程回调函数
HANDLE hTread;
DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
while (1)
{
std::cout << (char*)lpParameter << std::endl;
SuspendThread(hTread);//挂起线程,此时线程内的代码不再继续执行
}
return 0;
}
- 挂起进程(非必须,在需要避免线程空耗cpu时使用)
//第一种:线程函数内使用
Sleep(1);//运行到此语句时,该线程剩余时间片放弃,下次还会轮到时间片
//第二种:挂起和恢复
SuspendThread(hThread);//挂起线程,此时线程内的代码不再继续执行,必须恢复线程才能继续执行
ResumeThread(hThread); //恢复线程,此时线程内的代码根据上次保存的进程上下文,继续执行
- 退出线程,可以自行测试
退出方式 | DLL通知 | 线程栈 | 局部对象析构 |
---|---|---|---|
线程函数return | 收到 | 释放 | 调用 |
ExitThread | 收到 | 释放 | 不调用 |
TerminateThread | 没有 | 不释放 | 不调用 |
结束进程 | 没有 | 释放 | 不调用 |
TerminateThread(hThread, 0);
ExitThread(0); //严禁在主程序使用,会导致主线程退出但进程仍未结束的情况