Windows的线程使用
2019年5月21日
12:12
和Linux不同,Windows的线程在执行结束以后默认不需要进程中的函数来引导销毁,而是由操作系统来自动销毁内存空间。
内核对象
操作系统创建的资源(Resource)有很多种,进程,线程,文件,信号量,互斥量等。他们都有一个共同点,"都是由Windows操作系统创建并管理的资源"。
不同的资源类型在"管理"的方式上存在有差异。操作系统为了记录管理资源的相关信息,在其内部生成了数据块(结构体变量)。由于每种资源需要维护的信息不同,所以每种资源拥有的数据块格式也具有差异。这类数据称之位"内核对象"。
所以可以理解,"内核对象"就是Windows操作系统为了管理系统资源而创建的资源信息对象。
内核对象的所有者是内核(操作系统)。内核对象的创建、管理、销毁时机等工作都有操作系统来完成。
基于Windows的线程创建
现代操作系统都在系统级别上支持线程的运行。程序开始运行以后,调用main函数的主体是线程。main函数的运行基于线程来完成。进程承载线程的运行。
- 非显式创建线程的程序(如select服务器端)可以描述符:单一线程模型的应用程序
- 显示创建线程的程序可以描述为:多线程模型的应用程序
windows创建线程的函数
系统函数
#include <windows.h>
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,
SIZE_T dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreateionFlags,
LPDWORD lpThreadID
);
->>成功时返回线程句柄,失败时返回NULL
lpThreadAttributes:线程安全相关信息,NULL为默认设置
dwStackSize:分配给线程的栈大小,传递0生成默认大小
lpStartAddress:线程的main函数
lpParameter:线程main函数的参数
dwCreationFlags:指定线程创建后的行为,传递0,线程创建后立即进入可执行状态
lpThreadld:用于保存线程ID的变量地址
标准函数
#include <process.h>
uintptr_t _beginthreadex(
void * security,
unsigned stack_size,
unsigned (* start_address)(void*),
void * arglist,
unsigned initflag,
unsigned * thrdaddr
);
->>成功时返回线程句柄,失败时返回0
如果线程需要使用C/C++标准函数,则需要通过使用_beginthreadex函数来创建线程。与CreateThread函数相比,参数意义及调用顺序都没有区别。只是数据类型有所差异,需要适量修改。
句柄、内核对象与ID间的关系:
- 通过句柄区分内核对象
- 内核对象拥有资源的相关信息(通过内核对象区分线程)
- 线程ID用于区分操作系统创建的所有线程
内核对象的两种状态
资源类型不同,内核对象也含有不同的信息。其中,应用程序实现过程中需要特别关注的信息被赋予某种"状态(state)"。线程内核对象中需要重点关注线程是否已经终止。终止状态又称为"signaled状态",未终止状态称为"non-signaled状态"。
进程和线程的初始化状态时non-signaled状态。内核对象中带有一个boolean变量,其初始值为FALSE,当发生约定情况(进程/线程结束),该值变为TRUE,即为signaled状态。
验证signaled状态(线程结束)
#include <windows.h>
DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds);
->>成功时返回时间信息,失败时返回WAIT_FAILED
hHandle:内核对象句柄
dwMilliseconds: 毫秒为单位的等待时间,传递INFINITE阻塞
返回值:进入signaled状态返回WAIT_OBJECT_0,超时返回WAIT_TIMEOUT
该函数由于发生事件(signaled)返回时,有时会把相应内核对象再次变为non-signaled状态,具有这种特性的内核对象称为"auto-reset模式"的内核对象,而不会自动跳转的内核对象称为"manual-reset模式"的内核对象。
验证多个内核对象的signaled状态
#include <windows.h>
DWORD WaitForMUltipleObjects(
DWORD nCount, const HANDLE * lpHandles, BOLL bWaitAll,
DWORD dwMilliseconds);
->>成功时返回事件信息,失败时返回WAIT_FAILED
nCount:内核对象总数
lpHandles:内核对象句柄数组
bWaitAll:TRUE,等待全部。FALSE,有一个就返回
dwMilliseconds:等待时间,毫秒为单位,传递INFINITE阻塞