Windows线程基础

1、基础概念

进程:操作系统为用户的每个需求活动所开辟的任务,允许用户同时在操作系统上执行多个任务。如果进程内执行某个操作让该进程暂停,那么就会切换进程,执行别的进程或等待。
进程改进:在一个任务内会有很多的内部小任务,如果这些小任务互相之间也需要共同进行,当一个进程内的某个小任务暂停,就会在进程内切换小任务或者(对于分时系统)时间耗尽,切换进程
线程:加入线程的机制后,会让用户的操作任务更加灵活,而且可以更大限度的减少进程间的切换,(进程间的切换会耗费很多资源,而且浪费CPU时间)

进程的切换:现在进程正在执行,当要进行切换时,把该进程的数据资源、寄存器、堆栈、内存空间的使用、进程当前执行的代码位置等,给这些所有东西拍个照片做个记录,然后把要切换的那个任务的所有照片拿出来,一 一进行恢复,然后CPU就切换到该进程执行,进程就被切换掉
线程的切换:线程是属于进程内部的资源,同时也属于内核资源,线程被称为“轻量级线程”,就是因为切换时不会像进程那样附带很多资源信息,导致拍照繁琐,照片量大,整个过程消耗时间长,所以线程的切换比进程要快的多,也很轻巧,所以线程是操作系统中可执行的最小任务
Windows是多任务分时操作系统,支持进程和多线程。

进程组成
1、进程内核对象
2、地址空间
线程组成
1、线程的内核对象,操作系统用它管理线程
2、线程栈,用于维护线程执行时所需的所有参数和局部变量

每个进程至少有一个线程。
进程是有惰性的,进程从来不执行任何东西,只是一个线程的容器 ,线程在其进程的地址空间内执行代码和处理数据,这些线程将共享内核对象句柄。

2、何时创建线程

线程描述了进程内部的一条执行线路,每次初始化进程时,系统都会创建一个主线程。
对于用Microsoft C/C++编译器生成的应用程序,这个线程首先会执行C/C++ 运行库的启动代码,后者调用入口点函数(_tmain 或 _tWinMain),并继续执行,直至入口点函数返回C/C++ 运行库的启动代码,后者最终调用ExitProcess。
主线程是应用程序必须的,同时可以创建其它线程进行辅助工作

3、编写第一个线程函数

每个线程都必须有一个入口点函数,这是线程执行的起点,主线程入口点:(_tmain 或 _tWinMain),所以创建的线程也必须要有一个入口函数:

DWORD WINAPI ThreadFunc(LPVOID param){
	DWORD dwResult = 0;
	......
	return (dwResult );
}

最后线程函数将终止并返回,此时线程将终止运行,用于线程栈的内存也会被释放,
线程内核对象的使用计数也会递减,计数为0时,线程内核对象会被销毁。
线程函数的注意点:

  • 线程入口函数由我们自己指定,应避免重复
  • 线程函数只有一个参数
  • 线程函数必须返回一个值,作为该线程的退出代码
  • 线程函数应尽量使用函数参数和局部变量,因为函数的参数和局部变量是在线程栈上创建的。使用静态/全局变量时,多个线程可以同时访问,这样可能会破坏数据。

4、CreateThread 函数

知道如何实现线程函数后,接下来要让操作系统创建一个线程来执行线程函数
调用CreateThread 函数,系统会创建一个线程内核对象,这个内核对象是一个数据结构,操作系统用这个结构来管理线程。
系统从进程的地址空间中分配内存给线程栈使用,新线程在与负责创建的那个线程在相同的进程上下文中运行。因此,新线程可以访问内核对象的所有句柄、进程中的所有内存、同一进程的其它线程栈,这样同进程的线程间可以很容易通信

//参数中有很多默认,是因为在CreateProcess时,就已经CreateThread初始化过进程的主线程
HANDLE CreateThread(
		LPSECURITY_ATTRIBUTES lpThreadAttributes,
		//1、用来描述线程的安全属性的指针,NULL表示使用默认的安全描述符
		DWORD dwStackSize, 
		//2、分配该线程的堆栈大小,NULL表示与创建自己的线程相同
		LPTHREAD_START_ROUTINE lpStartAddress,
		//3、线程的入口函数地址
		LPVOID lpParameter, 
		//4、传递给线程的参数
		DWORD dwCreationFlags, 
		//5、NULL表示立即调度,CREATE_SUSPEND表示挂起
		LPDWORD lpThreadId 
		//6、线程相关的标识符返回(新线程的ID,一般为NULL)
		);

5、终止运行线程
线程可以通过以下4中方法终止运行:

  • (自然死亡法)线程函数返回

让线程函数返回,可以确保清理以下:
1、线程函数中创建的所有C++对象的应用程序清理工作都得以执行
2、操作系统正确释放线程栈使用的内存
3、操作系统把线程的退出代码设为线程函数的返回值
4、系统递减线程的内核对象的使用计数

  • (无奈死亡法)线程通过调用ExitThread函数杀死自己
VOID ExitThread(DWORD dwExitCode);

该函数将终止线程的运行,并导致操作系统清理该线程使用的所有操作系统资源,但是你的C/C++资源(如C++类对象)不会被销毁。

  • (突然死亡法)同一进程或另一进程中的线程调用 TerminateThread函数
BOOL TerminateThread(
	HANDLE hThread,	//标识了要终止的那个线程的句柄
	DWORD dwExitCode );	//线程终止运行时,其退出代码将变成你作为dwExitCode 参数传递的值,线程的内核对象使用计数递减

不同于ExitThread总是杀死主调线程,TerminateThread能杀死任何线程。
该函数是异步的,就是在线程函数返回前,线程已经终止,所以应加上WaitForSingleObject函数

  • (意外死亡法)包含线程的进程终止运行

当进程终止时(ExitProcss、TerminateProcess),会终止该进程内所有的线程,强制杀死所有线程,释放进程所有的资源进行清理

一旦线程不在运行,系统中就没有别的线程再用该线程的句柄,其他线程可以调用GetExitCodeThread函数来检查线程是否已经终止运行,其退出代码是什么。如果线程未终止,退出代码为STILL_ACTIVE标识符(0x103),函数调用成功返回TRUE。

BOOL GetExitCodeThread(
	HANDLE hTread,	//要进行检查的线程的线程句柄
	PDWORD pdwExitCode);	//保存其退出代码

6、线程内幕

在这里插入图片描述


CreateThread调用后,创建一个线程内核对象:
1、对象使用计数为:2(为0销毁线程)
2、线程挂起计数为:1(为0是可调度)
3、退出代码为:STILL_ACTIVE(0x103)
4、对象被设置为未触发状态
5、从进程的地址空间分配线程的堆栈,系统会把(线程函数的参数、线程函数入口地址)首先压入堆栈,(堆栈增长方向:高地址->低地址)
6、每个线程都有自己的一组CPU寄存器,称为线程的上下文(CONTEXT结构WinNT.h)

相关文章

文章浏览阅读2.2k次,点赞6次,收藏20次。在我们平时办公工作...
文章浏览阅读1k次。解决 Windows make command not found 和...
文章浏览阅读3.2k次,点赞2次,收藏6次。2、鼠标依次点击“计...
文章浏览阅读1.3w次。蓝光版属于高清版的一种。BD英文全名是...
文章浏览阅读974次,点赞7次,收藏8次。提供了更强大的功能,...
文章浏览阅读1.4w次,点赞5次,收藏22次。如果使用iterator的...