[C++ 网络协议] Windows平台下的线程

目录

1.内核对象( Windows)

2. 创建线程( Windows)

3. 线程内核对象的两种状态

3.1 内核对象状态的查看


1.内核对象( Windows)

在介绍Windows的线程之前,先介绍下Windows的内核对象。

内核对象的概念:

如线程、进程、文件、信号量、互斥量等等,这些都是由操作系统所创建的资源,也统一由操作系统来管理,操作系统为了方便管理它们,就会在创建它们的同时,生成数据块(也可视为结构体变量),这个数据块以记录相关信息的方式来管理各种资源,被称为“内核对象”。

内核对象的归属:

线程、文件等资源的创建请求都在进程中执行,但不能认为此时创建的内核对象所有者就是进程。其实,可以通过内核对象的概念很容易得出,内核对象的所有者是内核,而内核就是操作系统。

总结:

内核对象就是为了管理线程、文件等资源而由操作系统创建的数据块,其创建者和所有者均为操作系统。

2. 创建线程( 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在跨进程的范围内不会出现重复。

3. 线程内核对象的两种状态

一种是:signaled状态,表示线程已终止

一种是:non_signaled状态,表示线程未终止

操作系统会把这种状态信息保存到内核对象里,所有进程和线程的内核对象初始状态都是non_signaled,其通过1个boolean变量来表示,当为FALSE时,为non_signaled状态,当为TRUE时,为signaled状态。默认为FALSE,线程/进程结束,就会置为TRUE。这个状态不是一致的,内核对象类型不同,进入的状态的情况也不同。

3.1 内核对象状态的查看

单个内核对象状态的查看:

#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

你可能感兴趣的:(网络协议,c++,开发语言)