Windows 多线程编程总结

keyword:多线程 线程同步 线程池 内核对象

1 内核对象

1 .1 内核对象的概念

内核对象是内核分配的一个内存块,这样的内存块是一个数据结构,表示内核对象的各种特征。而且仅仅能由内核来訪问。应用程序若须要訪问内核对象,须要通过操作系统提供的函数来进行,不能直接訪问内核对象( Windows 从安全性方面来考虑的)。

内核对象通过 Create* 来创建,返回一个用于标识内核对象的句柄,这些句柄 (而不是内核对象)可在创建进程范围内使用,不可以被传递到其它进程中被使用。

1 .2 内核对象使用的计数

由于内核对象的全部者是内核,而不是进程,所以何时撤销内核对象由内核决定,而内核做这个决定的根据就是该内核对象是否仍然被使用。那么怎样推断内核对象是否被使用呢?能够通过内核对象的“使用计数”属性,一旦这个值变成 0 了,内核就能够释放该对象了。

1 .3 创建内核对象

1 .3.1 进程与句柄表

每一个进程在初始化的时候,将被分配一个句柄表,该句柄表中仅仅存储内核对象的句柄,不存储用户对象的句柄。句柄表的具体结构微软没有发布,可是大致包括三个内容:内核对象句柄,内核对象地址,訪问屏蔽标志。

微软为何要将内核对象的句柄设置为进程相关的呢?理由有:

l          不同的进程对内核对象的訪问权限是不同的,有必要区分对待

l          假设句柄是全局的,则一个进程能够控制另外一个进程的句柄,破坏另外一个进程的句柄。

1 .3.2 创建内核对象及操作系统内部机制

利用 CreateSomeObject 的函数来创建内核对象。调用该函数的时候内核就为该对象分配一个内存块,并进行初始化,然后内核再扫描该进程的句柄表,初始化一条记录并放在句柄表中。

1 .3.3 进程中使用内核对象的内部机制

如果函数 F 使用某个内核对象,其參数为 Handle1 ,则该函数内部须要查找该进程的句柄表,找出參数句柄相应的记录,然后才干使用该内核对象。

1 .4 关闭内核对象

不管进程如何创建内核对象,在不使用该对象的时候都应当通过 Bool CloseHandle(HANDLE hobj) 来向操作系统声明结束对该对象的訪问。为什么叫声明呢?是由于此时或许还有其它进程对该对象的訪问,操作系统可能并不马上释放该对象。操作系统须要做的是:从进程的句柄表中删除该内核对象的记录,另外再考察该内核对象的使用计数以决定是否须要释放该对象。

1 .5 内核对象的共享

说到共享,与之孪生的就是共享权限。 Windows 内核对象的共享有三种方式:

1 .5.1 继承式共享(父子进程间)

仅仅有当进程是父子关系的时候,才干使用此种方式的共享。特别要注意的是继承的是内核对象的句柄,内核对象本身是不具备继承性。要达到这样的继承的效果须要做下面几件事:

l          在进程创建内核对象的时候,须要一个安全结构 sa ( SECURITY_ATTRIBUTES 类型,以向 OS 声明对象的訪问方式)作为參数。继承式共享须要将结构的成员 sa.bInheritHandle 设置为 TRUE 。此时 OS 内部的处理式将进程的句柄表中的该对象的訪问屏蔽字段设置成“可继承”。

l          在创建子进程( CreateProcess 函数)时,设置创建參数 bInheritHandles 为 TRUE 。表示被创建的子进程能够继承父进程中的全部可继承内核对象。 OS 内部的处理是:复制父进程句柄表中的记录到子进程的句柄表中,并使用同样的句柄值;为内核对象的使用计数器加 1 。

特别说明:子进程可以继承的的内核对象仅局限于父进程创建它的时候所拥有的可继承内核对象。子进程诞生后,父进程再搞出什么可继承的东西,子进程是不能用的。这就须要在子进程中使用继承的内核对象的时候须要谨慎,以确定内核对象是否已被继承了。

利用 SetHandleinformation 方法能够随时改动内核对象句柄的一些属性,眼下公开的句柄属性有两种,一种是该句柄能否被继承,还有一种是该句柄能否被关闭。

1 .5.2 同名共享

同名共享,不须要共享进程之间存在父子关系。但局限于内核对象是否支持这样的共享方式。创建内核对象的 Create 函数中是否包括 pszName 是该内核对象是否支持同名共享的标志。

l          方法一:当 Process1 通过 CreateObject ( …”someName” )创建了一个名字为 someName 的内核对象后, Process2 也调用了 CreateObject ( …”someName” ),此时内核的动作是:在全局中查询发现已经存在 someName1 的对象;为 Process2 的句柄表加入一条 Ojbect 的记录,使用的句柄不确定;为 someName 这个 Object 的引用计数器加 1 。

l          方法二: Process2 使用 OpenObject ( …”someName” )的方式来获得对名 someName 的 Object 的句柄。用这样的 Open 方法的时候,须要提供一个參数让 OS 鉴权,以判定是否可以以參数指定的方式来訪问内核对象。

1 .5.3 复制内核对象的句柄的方式共享

跨进程边界的内核对象共享的另外一个方法是通过 DuplicateHandle 来复制内核对象句柄。

假设要将 ProcessS 中的对象复制到 ProcessT 中则调用 DuplicateHandle 的进程一定要有对这两个进程的訪问权,即句柄表中拥有这两个进程内核对象的句柄记录。

2 线程的一般概念

2 .1 视图

l          进程仅仅是线程的容器,从来不运行不论什么东西

l          线程总是在某个进程中被创建

l          线程在进程的地址空间中运行代码

l          线程们共享进程中的全部内核对象

3 线程的创建

HANDLE CreateThread(

PSECURITY_ATTRIBUTES psa,

DWORD cbStack,

PTHREAD_START_ROUTINE pfnStartAddr,

PVOID pvParam,

DWORD fdwCreate,

PDWORD pdwThreadID);

《 Windows 核心编程》 P124 介绍说应当使用编译器提供的线程创建函数,而不应当直接使用 CreateThread 。

3 .1 CreateThread 调用的内核行为

调用 CreateThread 后, OS 进行例如以下几个动作:

l          生成一个线程内核对象

l          在进程空间内为线程分配堆栈空间

由于线程的环境同于其所在进程的环境,所以创建的线程能够訪问进程中的全部资源,包含线程中全部的内核对象。

4 线程销亡

4 .1 终止线程的方式:

l          线程函数返回(最好使用这个方式,能够保证:线程种创建的 C++ 对象正常析构; OS 释放线程堆栈内存; OS 将线程的退出码设置为线程函数的返回值;系统将递减该线程内核对象的的使用计数器【假设此时还有其它引用 …… ,见以下说明】。)

l          调用 ExitThread (不能释放 C++ 对象,所以最好不要使用这个方式。另外,假设非要调用也应当调用编译器推荐的,如 _endThread 【 Windows 核心编程 P127 】)

l          同进程内的其它线程(包含主线程)调用 TerminateThread (被撤销线程得不到通知,不能释放资源,尽量避免这样的方式。另外这个函数是个异步函数,返回时,线程不保证已经被撤销,假设要观察线程是否被撤销,应当使用 WaitForSingleObject )

l          包括线程的进程终止(应当避免这样的方式)

4 .2 线程退出时 OS 的行为

l          线程内的全部用户对象被释放。

l          线程的退出码从 STILL_ACTIVE 改为传递给 ExitThread 或 TerminateThread 的代码

l          线程内核对象的状态改为“已通知”

l          假设线程为进程中的最后一个线程,则 OS 将进程当作已终止执行

l          线程内核对象的引用计数器减 1 (一旦线程终止了,其它引用改线程内核对象将不可以处理改线程的句柄,可是可以通过调用 GetExitcodeThread 来检查 hThread 代表的线程是否已经终止执行了。)

5 线程同步

5 .1 线程同步的起因以及解决之道

5 .1.1 共用资源型:多个线程须要訪问同一个资源的时候,为了保证资源不被破坏,须要线程对资源的訪问具有原子性。

5 .1.2 依赖型:一个线程等待另外一个线程某件事情完毕后才干运行 _ 能够通过手动事件的方式互相通知。

5 .2 线程同步种类细分

同步起因

同步种类

同步方法备注

共用资源

多个线程对共用变量做加减操作

互锁函数族之: InterlockedExchangeAdd

共用资源

多个线程对公共变量、指针做赋值操作

互锁函数族之: InterlockedExchange ,InterlockedExchangepoint

共用资源

多个线程须要依据对公共变量、指针的推断做操作选择

互锁函数族之: InterlockedCompareExchange

InterlockedCompareExchangePointer

共用资源

复杂数据结构(非单值),不适合互锁函数族处理的

用“关键代码”的方式,关键代码中要注意 1 、要尽量的高速处理完,以防止其它等待线程等待太长时间 2 、线程等待过程中由用户模式切换到内核模式,耗费 1000 个 CPU 周期,时间比較长。 3 、仅仅能对单个进程中的线程进行同步)

InitializeCriticalSection;

EnterCriticalSection;

LeaveCriticalSection;

DeleteCriticalSection;

处理线程同步的一种方法

对线程同步做的一个抽象,线程的同步本质上都是依赖于某个其它事件的发生,用软件的方法来对所依赖的事件做一个抽象,将有助与程序编写的简捷

CreateEvent

Event 的重要属性有一个是“自己主动” or “手动”,假设是自己主动的,则在某个线程用 Wait ××成功等待到事件的“通知”状态后,则事件状态立马变成“未通知”状态,以保证同一时候对资源訪问的线程仅仅有一个。

原则上不算线程同步范畴,而属于对 wait** 的一种应用方式

一个能够作为定时器的内核对象, Waitable Timer

CreateWaitableTimer

SetWaitableTimer,

CancelWaitableTimer

共用资源

一组线程对一组相同性质的资源的争用,则这组资源须要有所表示,以告知线程们是否有空暇的给以为他们服务,以信号量机制实现

CreateSemaphore,ReleaseSemaphore

共用资源

一组线程对一个单一的资源的争用,须要有一种机制保证同一个事件仅仅有一个线程能得到资源。以 Mutex 方式实现

CreateMutex

ReleaseMutex

与关键代码的区别在于:

1、    同意不同进程的线程之间同步

2、    内核对象,用户模式和内核模式切换的时候须要很多其它的 CPU 开销

特别说明: WaitForSingleObject/WaitForMultipleObject 是抑制线程本身的一种手法,配合以共用资源对象或所依赖的其它对象“通知状态”的原子性变化,以达到线程在争用资源、互相依赖时运行的顺序化,从而达到同步的目的。

综上:事实上 Windows 的线程同步机制是提供了一组不同情况下的资源争用处理办法而已。与此同一时候推出的 Wait ××却能够带来非常多其它优点,甚至部分缓解 C++ 语言没有事件机制的缺憾,部分达到了 JAVA,C# 中事件机制的效果,为 Oberserve 模式的实现做了些贡献。

Windows多线程编程总结相关推荐

  1. Windows平台下的多线程编程

    线程是进程的一条执行路径,它包含独立的堆栈和CPU寄存器状态,每个线程共享所有的进程资源,包括打开的文件.信号标识及动态分配的内存等.一个进程内的所有线程使用同一个地址空间,而这些线程的执行由系统调度 ...

  2. Win32多线程编程(1) — 基础概念篇

    内核对象的基本概念 Windows系统是非开源的,它提供给我们的接口是用户模式的,即User-Mode API.当我们调用某个API时,需要从用户模式切换到内核模式的I/O System Servic ...

  3. windows多线程同步互斥--总结

    2019独角兽企业重金招聘Python工程师标准>>> 秒杀多线程面试题系列 参考JustDoIT -- 大部分内容 <Windows核心编程>线程同步对象速查表 对象 ...

  4. linux多线程编程和linux 2.6下的nptl,Linux多线程编程和Linux 2.6下的NPTL

    这几天由于工作需要,琢磨了一下Linux下的多线程的相关资料.Linux下最常用的多线程支持库为Pthread库,它是glibc库的组成部分.但是关于Pthread的说明文档非常缺乏,特别是对POSI ...

  5. Windows下多线程编程技术及其实现

    本文首先讨论16位Windows下不具备的线程的概念,然后着重讲述在32位Windows 95环境下多线程的编程技术,最后给出利用该技术的一个实例,即基于Windows95下TCP/IP的可视电话的实 ...

  6. C++使用Windows API CreateMutex函数多线程编程

    C++中也可以使用Windows 系统中对应的API函数进行多线程编程.使用CreateThread函数创建线程,并且可以通过CreateMutex创建一个互斥量实现线程间数据的同步: #includ ...

  7. C语言中pthread或Windows API在多线程编程中的基本应用

    文章目录 多线程概述 掌握多线程需要学习什么? 使用pthread.h实现多线程 使用Windows API实现多线程 使用threads.h实现多线程 参考资料 警告 由于我懒得写完,而且懂的也不是 ...

  8. (C#)安全、简单的Windows Forms多线程编程 (一)

    (C#)安全.简单的Windows Forms多线程编程  (一) Chris Sells June 28, 2002 翻译:袁晓辉 www.farproc.com farproc#AT#gmail# ...

  9. Windows下多线程编程

    前言 熟练掌握Windows下的多线程编程,能够让我们编写出更规范多线程代码,避免不要的异常.Windows下的多线程编程非常复杂,但是了解一些常用的特性,已经能够满足我们普通多线程对性能及其他要求. ...

最新文章

  1. Bootstrap笔记
  2. 计算机一级必考知识点,计算机一级word操作知识点
  3. js获取被点击的元素以及子元素
  4. java 布尔表达式_java - 布尔值,条件运算符和自动装箱
  5. AOP in dotnet :AspectCore的参数拦截支持
  6. 32. 脱壳篇-简单带壳的程序、反调试带壳的程序(堆栈平衡原理找OEP、代码段设置断点)
  7. ZZULIOJ 1074:百钱买百鸡
  8. C++:类占用的字节内存
  9. 双刃剃须刀行业调研报告 - 市场现状分析与发展前景预测
  10. 建筑电气工程设计常用图形和文字符号_建筑电气施工图设计正误案例对比
  11. step7-micro/win 在win10系统下安装步骤
  12. react 输入框 回车事件切换
  13. ModelsimSE debussy
  14. mac pro M1(ARM)安装:centos8.0虚拟机
  15. 如何使用DD-WRT增强Wi-Fi网络信号并增加范围
  16. SpringBoot集成Liquibase
  17. 软件测试方法和技术第一章——引论
  18. 操作系统之死锁检测算法:银行家算法
  19. Error in render: TypeError: Cannot read property 'XXXXX' of undefined
  20. 设置elementui表格的上下左右滚动条

热门文章

  1. python 实现81个人脸关键点实时检测
  2. UVa12412 - A Typical Homework (a.k.a Shi Xiong Bang Bang Mang)
  3. TCP/IP协议:概述
  4. CocosCreate粒子系统白边问题
  5. 如何控制dedecms描述的长度?
  6. HTML布局篇之双飞翼(圣杯)布局
  7. 几句话说明 .NET MVC中ViewData, ViewBag和TempData的区别
  8. 使用Sencha Designer来快速开发web用户界面 -- 初识Designer
  9. Java中读取控制台输入
  10. Redhat7.2上编译Linux内核源码