Windows多线程编程总结
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 线程同步种类细分
特别说明: WaitForSingleObject/WaitForMultipleObject 是抑制线程本身的一种手法,配合以共用资源对象或所依赖的其他对象“通知状态”的原子性变化,以达到线程在争用资源、互相依赖时执行的顺序化,从而达到同步的目的。
综上:其实 Windows 的线程同步机制是提供了一组不同情况下的资源争用处理办法而已。与此同时推出的 Wait ××却可以带来很多其他好处,甚至部分缓解 C++ 语言没有事件机制的缺憾,部分达到了 JAVA,C# 中事件机制的效果,为 Oberserve 模式的实现做了些贡献。
Windows多线程编程总结的更多相关文章
- windows多线程编程星球(一)
以前在学校的时候,多线程这一部分是属于那种充满好奇但是又感觉很难掌握的部分.原因嘛我觉得是这玩意儿和编程语言无关,主要和操作系统的有关,所以这部分内容主要出现在讲原理的操作系统书的某一章,看完原理是懂 ...
- Windows多线程编程入门
标签(空格分隔): Windows multithread programming 多线程 并发 编程 背景知识 在开始学习多线程编程之前,先来学习下进程和线程 进程 进程是指具有一定独立功能的程序在 ...
- windows多线程编程实现 简单(1)
内容:实现win32下的最基本多线程编程 使用函数: #CreateThread# 创建线程 HANDLE WINAPI CreateThread( LPSECURITY_ATTRIBUTES lpT ...
- windows多线程编程(一)(转)
源出处:http://www.cnblogs.com/TenosDoIt/archive/2013/04/15/3022036.html CreateThread:Windows的API函数(SDK函 ...
- windows多线程编程
进程共同实现某个任务或者共享计算机资源, 它们之间存在两种关系: 1.同步关系, 指为了完成任务的进程之间, 因为需要在某些位置协调它们的执行顺序而等待, 传递消息产生的制约关系. 2.互斥关系, 进 ...
- Windows多线程编程入门笔记
每次处理并行任务时,如果要等待用户输入或依赖外部(如与灿亨控制器响应),就应该为类似的操作单独创建一个线程,这样我们的程序才不会挂起无响应. 静态库和动态库 静态库是指在程序运行前就编译完成的库,如# ...
- Windows平台下的多线程编程
线程是进程的一条执行路径,它包含独立的堆栈和CPU寄存器状态,每个线程共享所有的进程资源,包括打开的文件.信号标识及动态分配的内存等.一个进程内的所有线程使用同一个地址空间,而这些线程的执行由系统调度 ...
- Win32多线程编程(1) — 基础概念篇
内核对象的基本概念 Windows系统是非开源的,它提供给我们的接口是用户模式的,即User-Mode API.当我们调用某个API时,需要从用户模式切换到内核模式的I/O System Serv ...
- 【转】Windows的多线程编程,C/C++
在Windows的多线程编程中,创建线程的函数主要有CreateThread和_beginthread(及_beginthreadex). CreateThread 和 ExitThread 使 ...
随机推荐
- ViewPager 基本方式加载view
1.新建view_pager.xml <?xml version="1.0" encoding="utf-8"?> <RelativeLayo ...
- Maven——使用Maven构建多模块项目
原文:http://www.cnblogs.com/xdp-gacl/p/4242221.html 在平时的Javaweb项目开发中为了便于后期的维护,我们一般会进行分层开发,最常见的就是分为doma ...
- 正则表达式使用(Js、Java)
Js中全局替换,需要在最后加上g(global),并且使用//包围起来 1.全局替换字符+ 和 只替换第一个字符+ alert("2014+03-22++aaaa".replace ...
- qq被冻结怎么激活
原文章http://jingyan.baidu.com/article/ce436649f43d4d3773afd3f2.html 一些QQ用户可能遇到过QQ被冻结的情况吧!不用着急,小编分享QQ被冻 ...
- VI经典插件ctags
Vi经典插件ctags(转) (为了提高工作效率,必须学会使用一些工具) . 查看 key 相关信息说明的命令 :help keycodes ============================= ...
- u盘安装ubuntu server 14.04 以及No CD-ROM drive was detected 错误
u盘安装ubuntu server 14.04 1:下载ubuntu server14的 iso镜像文件 2:下载 UltraISO U盘镜像制作工具 : 3:使用Ultra iOS 将下载好的 is ...
- dedecms5.7怎么取消邮箱验证以及dedecms 会员发布的文章不需要审核的解决方法
后台 ——系统基本参数——会员设置——会员权限开通状态——改为0 1.实现会员发布文章不需要审核,非会员发布需要审核 在member这个文件夹下找到archives_sg_add.php这个文件,打开 ...
- JAVA通过C3P0连接数据库
配置文件: <?xml version="1.0" encoding="UTF-8"?> <c3p0-config> <na ...
- HTML5中的文本级语义
<p>这篇文章的发布时间是<time datetime="2016-02-26T16:30+08:00" pubdate>今天</time>&l ...
- C#中操作txt,抛出“正由另一进程使用,因此该进程无法访问此文件”
将你的File.Create(fileName); //创建fileName路径的文本改为 1 2 3 using (FileStream fs = File.Create(fileName)){} ...