3.Windows核心编程-线程及线程调度

线程

  • 与进程类似,线程也由两个组件组成:
    • 一个是线程的内核对象,操作系统用它管理线程。内核对象还是系统用来存放线程统计信息的地方;
    • 一个线程堆栈,用于维护线程执行时所需的所有函数参数和局部变量。
//线程函数

//创建线程
HANDLE CreateThread(
    PSECURITY_ATTRIBUTES    psa,            //线程安全属性
    DWORD                   cbStackSize,    // 线程堆栈大小,默认是 1MB(Itanium芯片上,默认大小是4MB)
    PTHREAD_START_ROUTINE   pfnStartAddr,   // 线程函数地址
    PVOID                   pvParam,        // 传入线程的参数
    DWORD                   dwCreateFlags,  // 控制线程的标志: CREATE_SUSPENDED: 创建之后挂起; 0: 创建之后立即执行
    PDWORD                  pdwThreadID);   // 返回值:线程ID



DWORD WINAPI ThreadFunc(PVOID pvParam)
{
    DWORD dwResult = 0;

    return dwResult;
}

线程可以通过以下4种方法来终止运行。
1. 线程函数返回(这是强烈推荐的)。
2. 线程通过调用ExitThread函数“杀死”自己
终止线程运行,并导致操作系统清理该线程的所有操作系统资源,但C/C++资源(如C++类对象)不会被销毁
3. 同一个进程或另一个进程中的线程调用TerminateThread函数
异步终止线程,线程终止时不会得到通知。ExitThread函数终止线程,线程的堆栈会被销毁,但是使用TerminateThread,除非拥有此线程的进程终止运行,否则系统不会销毁这个线程堆栈,Microsoft特意用这种方式来实现TerminateThread,其他还在运行的线程可引用被终止线程堆栈上的值。 动态链接库在线程终止运行时会收到通知,但被TerminateThread终止的线程,DLL不会收到这个通知,其结果不能执行正常的清理工作。
4. 包含线程的进程终止运行。

2.线程创建与初始化
对CreateThread函数的一个调用系统创建一个线程内核对象,该对象最初的使用计数为2(只有线程终止同时从CreateThread返回的句柄关闭,否则线程内核对象不会被销毁)该线程内核对象的其他属性也被初始化:暂停计数被设为1,退出代码被设计为STILL_ACTIVE(0x103),内核对象被设为nonsignaled状态。一旦创建了内核对象,系统就分配内存,供线程的堆栈使用,此内存是从进程的地址空间内分配的,因为线程没有自己的地址空间。系统将CreateThread函数的pvParam参数压入堆栈,然后将传给CreateThread函数的pfnStartAddr值压入堆栈。

每个线程都有自己的一组CPU寄存器,称为线程的上下文(context)。上下文反映了当线程上一次执行时,线程的CPU寄存器的状态。线程的CPU寄存器全部保存在一个CONTEXT结构。CONTEXT结构本身保存在线程内核对象中。线程始终在进程的上下文中运行。 当线程内核对象被初始化的时候,CONTEXT结构的堆栈指针寄存器被设为 pfnStartAddr 在线程堆栈中的地址,而指令指针寄存器被设为 RtlUserThreadStart 函数:

VOID RtlUserThreadStart(PTHREAD_START_ROUTINE pfnStartAddr, PVOID pvParam) {
    __try {
        ExitThread((pfnStartAddr)(pvParam));
    }
    __except(UnhandledExceptionFilter(GetExceptionInformation())) {
        ExitProcess(GetExceptionCode());
    }
// NOTE: We never get here.
}

线程完全初始化好之后,系统将检查CREATE_SUSPENDED标志是否传给CreateThread函数,如果没有传递,系统将线程的暂停计数递减至0,线程开始运行。

C/C++运行库

Microsoft Visual Studio附带的C/C++库:

库名称 描述
LibCMt.lib 库的静态链接Release版本
LibCMtD.lib 库的静态链接Debug版本
MSVCRt.lib 导入库,用于动态链接MSVCR80.dll库的Release版本
MSVCRtD.lib 导入库,用于动态链接MSVCR80D.dll库的Debug版本
MSVCMRt.lib 导入库,用于托管/原生代码混合
MSVCURt.lib 导入库,编译成百分之百纯MSIL代码

标准C运行库是1970年左右发明的,并没有线程的概念,标准C运行库的发明者也没有考虑到为多线程应用程序使用C运行库的问题。例如,标准C运行库一部分函数在出错时设置的是全局变量 errno,多线程环境出出问题的C/C++运行库变量和函数有:errno,_doserrno, strtok, _wcstok, strerror, _strerror, tmpnam, tmpfile, asctime, _wasctime, gmtime, _ecvt和_fcvt等等。
为了保证这些函数的正确执行,C/C++库必须在每个线程中为这些函数维护一个数据结构,并且还要保证此数据结构不会被其他线程修改,或是无意识的修改了其他线程的这个数据结构,使用C/C++运行库函数时,创建多线程用的函数版本是:_beginthreadex、_endthreadex ,该函数会为每个线程创建一个与线程挂钩的线程局部变量,这样线程之间就不会相互影响。

unsigned long _beginthreadex(
    void *security,
    unsigned stack_size,
    unsigned (*start_address)(void *),
    void *arglist,
    unsigned initflag,
    unsigned *thrdaddr)
{
    _ptiddata ptd; // 线程数据块指针
    uintptr_t thdl; // 线程句柄
    //分配线程数据块
    if ((ptd = (_ptiddata)_calloc_crt(1, sizeof(struct _tiddata))) == NULL)
        goto error_return;

    /
  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值