目录
1.内核对象( Windows)
2. 创建线程( Windows)
3. 线程内核对象的两种状态
3.1 内核对象状态的查看
在介绍Windows的线程之前,先介绍下Windows的内核对象。
内核对象的概念:
如线程、进程、文件、信号量、互斥量等等,这些都是由操作系统所创建的资源,也统一由操作系统来管理,操作系统为了方便管理它们,就会在创建它们的同时,生成数据块(也可视为结构体变量),这个数据块以记录相关信息的方式来管理各种资源,被称为“内核对象”。
内核对象的归属:
线程、文件等资源的创建请求都在进程中执行,但不能认为此时创建的内核对象所有者就是进程。其实,可以通过内核对象的概念很容易得出,内核对象的所有者是内核,而内核就是操作系统。
总结:
内核对象就是为了管理线程、文件等资源而由操作系统创建的数据块,其创建者和所有者均为操作系统。
方法一:
#include
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes, //线程安全相关信息,传NULL为默认设置
SIZE_T dwStackSize, //要分配给线程的栈大小,传0为默认大小
LPTHREAD_START_ROUTING lpStartAddress, //传递线程的main函数信息
LPVOID lpParameter, //调用main函数传递的参数
DWORD dwCreationFlags, //指定线程创建后的行为,传0表示线程进入可执行状态
LPDWORD lpThreadId //保存线程ID的变量的地址
);
成功返回线程句柄
失败返回NULL
只需考虑lpStartAddress参数(线程main函数信息),以及lpParameter参数(调用main函数传递的参数)即可,因为其他的都传0或NULL,除了lpThreadId。
缺点:通过这个方式创建出来的线程里,在使用C/C++标准函数时,会不稳定。
方法二:(线程安全的标准C函数)
#include
uintptr_t _beginthreadex(
void* security, //线程安全相关信息,传NULL为默认设置
unsigned stack_size, //要分配给线程的栈大小,传0为默认大小
unsigned (* start_address)(void* ), //传递线程的main函数信息
void* arglist, //调用main函数传递的参数
unsigned initflag, //指定线程创建后的行为,传0表示线程进入可执行状态
unsigned* thrdaddr //保存线程ID的变量的地址
);
);
成功返回线程句柄
失败返回0
_beginthread函数和_beginthreadex函数的区别:
前者会为了防止访问内核对象,让创建线程时返回的句柄失效。后者不会。
注意:方式二的线程的main函数,需要在函数名前加上WINAPI宏,其是Windows的固有关键字,用于指定参数传递方向,分配的栈返回方式等函数调用相关规定。如:
unsigned WINAPI ThreadFunc(void* arg)
{
......
}
int main()
{
......
HANDLE hTrread=(HANDLE)_beginthreadex(NULL,0,ThreadFunc,(void*)¶m,0,&threadId);
}
句柄、内核对象、线程ID的关系:
句柄可以引用内核对象,所以可以通过句柄来区分内核对象,通过内核对象可以区分线程。所以线程句柄成为可以区分线程的工具。
那么线程句柄可以区分线程,那线程ID有什么用?
线程ID也是用来区分线程的,但是它们的区别是:句柄的整数值在不同进程中可能会出现重复,但线程ID在跨进程的范围内不会出现重复。
一种是:signaled状态,表示线程已终止
一种是:non_signaled状态,表示线程未终止
操作系统会把这种状态信息保存到内核对象里,所有进程和线程的内核对象初始状态都是non_signaled,其通过1个boolean变量来表示,当为FALSE时,为non_signaled状态,当为TRUE时,为signaled状态。默认为FALSE,线程/进程结束,就会置为TRUE。这个状态不是一致的,内核对象类型不同,进入的状态的情况也不同。
单个内核对象状态的查看:
#include
DWORD WaitForSingleObject(
HANDLE hHandle, //查看状态的内核对象句柄
DWORD dwMilliseconds //以1/1000秒为单位指定超时时间,传递INFINITE会阻塞住,
//直到内核对象变为signaled状态
);
成功返回事件信息,事件信息:成功进入signaled状态返回WAIT_OBJECT_0,超时返回WAIT_TIMEOUT
失败返回WAIT_FAILED
该函数放回时,内核对象变为signaled状态,之后,有时会把相应内核对象又改为non-signaled状态。这种函数返回后自动切换回non-signaled状态的内核对象称为“auto-reset模式”的内核对象。反之,不会自动切换的内核对象就称为“manual-reset模式”的内核对象。
多个内核对象状态的查看:
#include
DWORD WaitForMultipleObjects(
DWORD nCount, //验证的内核对象数
const HANDLE* lpHandles, //存有内核对象句柄的数组地址值
BOOL bWaitAll, //TRUE,则所有内核对象都变为signaled时返回
//FALSE,则只要有一个验证对象的状态变为signaled时就返回
DWORD dwMilliseconds //以1/1000秒为单位指定超时时间,传递INFINITE会阻塞住,
//直到内核对象变为signaled状态
);
成功返回事件信息,事件信息:成功进入signaled状态返回WAIT_OBJECT_0,超时返回WAIT_TIMEOUT
失败返回WAIT_FAILED