当前位置: 首页 > news >正文

windows中结束线程的方式

windows操作系统中,创建的线程有以下4种终止方式

1. 线程函数返回

当线程函数返回时,该线程会被终止,始终应该采用该方式来结束线程的运行,因为这是确保所有线程资源被正确清除的唯一办法。如果线程按照该方式成功返回,则:

1)线程函数中创建的所有C++对象均能通过他们的析构函数正确的被撤销

2)操作系统将正确的释放线程运行栈所使用的内存

3)系统将线程的退出代码(在线程的内核对象中维护)设置为线程函数的返回值

4)系统将该线程内核对象的使用计数递减

2.ExitThread函数

可以调用该函数,强制终止线程的运行。该函数将导致操作系统清除该线程所使用的所有操作系统资源。但是,C++资源(如类对象)将不被撤销。该方法通常是windows用来撤销线程的函数。因此,最好采用方法1来退出线程,而不是通过调用该函数来返回。

3.TerminateThread函数

调用该函数也能终止线程的运行,但与ExitThread函数不同,后者总是撤销调用的线程,而前者能够撤销任何线程。TerminateThread是异步运行的函数,也就是说,它告诉系统你想要线程终止运行,但是,该函数返回时,不能保证线程被撤销。如果需要确切地知道该线程已经终止运行,必须调用WaitForSingleObject或类似函数。当使用方法1和方法2撤销线程时,该线程的内存栈空间也被撤销。而如果使用TerminateThread,那么在拥有该线程的进程终止运行之前,系统不撤销该线程的运行栈。

4.进程终止运行时终止线程

在进程终止运行时,该进程中的所有线程全部终止运行,由于整个进程已经被关闭,进程所使用的所有资源肯定已被清除。这当然包括所有线程的栈空间。使用ExitProcess和TerminateProcess函数,会导致进程中的剩余线程被强制撤销,就像从每个剩余的线程调用TerminataProcess一样,因此,这也意味着正确的应用程序清除没有发生,即C++对象析构函数没被调用,数据没有转移至磁盘等等。

总结以上4种方法,通常,我们应当始终采用第一种方法来终止线程的运行。

我们可以在主线程将要退出时,给子线程发消息,或者别的方式,告诉子线程,让子线程自动退出。

 

 

 

今天看了点关于Windows多线程的东西,摘抄点关于CloseHandle的内容放于此,以便以后参考。

主要是《Windows核心编程》里的两小节:

3.1.1 内核对象的使用计数
            内核对象由内核所拥有,而不是由进程所拥有。换句话说,如果你的进程调用了一个创建内核对象的函数,然后你的进程终止运行,那么内核对象不一定被撤消。在大多数情况下,对象将被撤消,但是如果另一个进程正在使用你的进程创建的内核对象,那么该内核知道,在另一个进程停止使用该对象前不要撤消该对象,必须记住的是,内核对象的存在时间可以比创建该对象的进程长。
            内核知道有多少进程正在使用某个内核对象,因为每个对象包含一个使用计数。使用计数是所有内核对象类型常用的数据成员之一。当一个对象刚刚创建时,它的使用计数被置为1。然后,当另一个进程访问一个现有的内核对象时,使用计数就递增1。当进程终止运行时,内核就自动确定该进程仍然打开的所有内核对象的使用计数。如果内核对象的使用计数降为0,内核就撤消该对象。这样可以确保在没有进程引用该对象时系统中不保留任何内核对象。
3.2.2 关闭内核对象
            无论怎样创建内核对象,都要向系统指明将通过调用C l o s e H a n d l e来结束对该对象的操作:该函数首先检查调用进程的句柄表,以确保传递给它的索引(句柄)用于标识一个进程实际上无权访问的对象。如果该索引是有效的,那么系统就可以获得内核对象的数据结构的地址,并可确定该结构中的使用计数的数据成员。如果使用计数是0,该内核便从内存中撤消该内核对象。如果将一个无效句柄传递给C l o s e H a n d l e,将会出现两种情况之一。如果进程运行正常,C l o s e H a n d l e返回FA L S E,而G e t L a s t E r r o r则返回E R R O R _ I N VA L I D _ H A N D L E。如果进程正在排除错误,系统将通知调试程序,以便能排除它的错误。
       在C l o s e H a n d l e返回之前,它会清除进程的句柄表中的项目,该句柄现在对你的进程已经无效,不应该试图使用它。无论内核对象是否已经撤消,都会发生清除操作。当调用C l o s e H a n d l e函数之后,将不再拥有对内核对象的访问权,不过,如果该对象的使用计数没有递减为0,那么该对象尚未被撤消。这没有问题,它只是意味着一个或多个其他进程正在使用该对象。当其他进程停止使用该对时(通过调用C l o s e H a n d l e),该对象将被撤消。
            假如忘记调用C l o s e H a n d l e函数,那么会不会出现内存泄漏呢?答案是可能的,但是也不一定。在进程运行时,进程有可能泄漏资源(如内核对象)。但是,当进程终止运行时,操作系统能够确保该进程使用的任何资源或全部资源均被释放,这是有保证的。对于内核对象来说,系统将执行下列操作:当进程终止运行时,系统会自动扫描进程的句柄表。如果该表拥有任何无效项目(即在终止进程运行前没有关闭的对象),系统将关闭这些对象句柄。如果这些对象中的任何对象的使用计数降为0,那么内核便撤消该对象。
            因此,应用程序在运行时有可能泄漏内核对象,但是当进程终止运行时,系统将能确保所有内容均被正确地清除。另外,这个情况适用于所有对象、资源和内存块,也就是说,当进程终止运行时,系统将保证进程不会留下任何对象。

以下是网友的评论:

CreateThread启动了一个线程,同时产生一个句柄让你好操纵这个线程,如果你不要用这个句柄了就CloseHandle关掉它。
调用这个CloseHandle并不意味着结束线程,而是表示不关心此句柄的状态了,也就无法控制子进程的线程了。如果需要关心,可以在子进程结束后再CloseHandle,但一定得CloseHandle。
操作系统内核管理内核对象的生命期,应用程序通过CloseHandle操作内核对象的引用计数,当引用计数由1降为0时,内核负责销毁相应的内核对象。进程和线程都有一个内核对象与它们对应,操作系统通过内核对象管理进程和线程。

本人总结:

当你在程序中,不需要再操作创建的线程时,就CloseHandle掉,即便是那个线程目前计数为1,等你调用CloseHandle后该计数降为0,但已经创建的线程并没有被马上撤消,而是等线程函数执行完毕后才撤消,或者是在线程函数执行完毕前整个进程结束,那么该线程也被撤消。至于为什么在计数降为0时,没有马上撤消该线程,不是很清楚。

 

今天(2007-08-28)又看了下《Windows核心编程》,有了些新的认识:

6.4:当CreateThread被调用时,系统创建一个线程内核对象。该线程内核对象不是线程本身,而是操作系统用来管理线程的较小的数据结构。………………这与进程和进程内核对象之间的关系是相同的。6.6:调用CreateThread可使系统创建一个线程内核对象。该对象的初始使用计数是2(在线程停止运行和从CreateThread返回的句柄关闭之前,线程内核对象不会被撤销)。

根据最后一条就可以解释我昨天的疑问了^_^

 

 

——————————————————————————————————————————————

一,在程序中建立线程的概念

      对于一个进程而言,在进程建立后,同时系统也会为进程自动分配一个主线程。拿Main函数而言,当Main函数执行完后,此时主线程就退出了,主线程退出也同时意味着进程结束。

二,线程、内核对象、内核对象引用计数

      1.创建一个线程有几种方法,这里我们先学习的是利用CreateThread()函数创建线程,此函数的参数及具体用法参见MSDN。如果创建线程成功,函数则返回一个新的线程句柄。(根据《Windows核心编程》,线程创建时,系统设置线程内核对象的引用计数为1,在Create函数返回前,将会打开线程句柄,所以线程的内核对象引用计数+1)

=================================================================================

CreateThread后那个线程的引用计数不是1,而是2。  
   
  creating   a   new   process   causes   the   system   to   create   a   process   kernel   object    
  and   a   thread   kernel   object.   At   creation   time,   the   system   gives   each   object    
  an   initial   usage   count   of   1.   Then,   just   before   CreateProcess   returns,   the    
  function   opens   the   process   object   and   the   thread   object   and   places   the    
  process-relative   handles   for   each   in   the   hProcess   and   hThread   members   of    
  the   PROCESS_INFORMATION   structure.   When   CreateProcess   opens   these   objects    
  internally,   the   usage   count   for   each   becomes   2.                                                  ---摘自《Windows核心编程》

=================================================================================

三,CloseHandle()用法

      1.CloseHandel(ThreadHandle );
只是关闭了一个线程句柄对象,表示我不再使用该句柄,对该句柄不感兴趣,即不对这个句柄对应的线程做任何干预了。并没有结束线程,线程本身还在继续运行。如果你CreateThread以后需要对这个线程做一些操作,比如改变优先级,被其他线程等待,强制TermateThread等,就要保存这个句柄,使用完了再CloseHandle()。

      2.为什么要CreateThread()和CloseHandle()紧挨配套使用

一方面,所有的内核对象(包括线程Handle)都是系统资源,用了要还的,也就是说用完后一定要CloseHandle关闭之,如果不这么做,你系统的句柄资源很快就用光了,另一方面,由于CreateThread()后线程内核对象的引用计数是2,在CloseHandle()引用计数-1之后,内核对象引用计数仍不为0,不会被释放,所以线程仍运行,直到线程函数执行完毕后,引用计数再-1,线程结束。


还可以查看这篇文章  http://hi.baidu.com/heiheijiushiwo/blog/item/02c29924e518e0318644f979.html

 

启发:

1 windows 核心编程 以后得看

2 系统管理线程是通过线程内部内核对象的引用计数 当此引用计数为0 时线程被关闭

 当在开始 createthread()创建完线程后线程的引用计数为2

调用closehadle()将只关闭不对线程做操作同时将线程引用计数减1

当在线程执行完毕时 引用计数在减1 正常情况下为 0  线程结束

相关文章:

  • mogodb
  • 22次课(yum更换国内源、yum下载rpm包、源码包安装、把源码包打包成rpm包)
  • mybatis 延迟加载
  • Python基础学习四 列表、元组、字典、集合
  • Mysql添加更新删除数据-表
  • 如何在本地测试Fabric Code
  • 状态码 301 与 302的区别
  • RHEL6 搭建LVS/DR 负载均衡集群 案例
  • “-Xmx1024m -Xms1024m -Xmn512m -Xss256k”——Java运行参数
  • Linux权限详解
  • Tomcat7 Redis Session共享
  • 练习题
  • C#Vs关于连接数据库的语句
  • openssl windows 生成公钥与私钥
  • 使用xUnit为.net core程序进行单元测试 -- Assert
  • [PHP内核探索]PHP中的哈希表
  • 【347天】每日项目总结系列085(2018.01.18)
  • Android 架构优化~MVP 架构改造
  • co模块的前端实现
  • CSS盒模型深入
  • HTML中设置input等文本框为不可操作
  • IndexedDB
  • leetcode388. Longest Absolute File Path
  • nginx(二):进阶配置介绍--rewrite用法,压缩,https虚拟主机等
  • PAT A1050
  • Redash本地开发环境搭建
  • 编写符合Python风格的对象
  • 多线程 start 和 run 方法到底有什么区别?
  • 分类模型——Logistics Regression
  • 通过来模仿稀土掘金个人页面的布局来学习使用CoordinatorLayout
  • 新手搭建网站的主要流程
  • 阿里云服务器购买完整流程
  • 京东物流联手山西图灵打造智能供应链,让阅读更有趣 ...
  • ​插件化DPI在商用WIFI中的价值
  • # Swust 12th acm 邀请赛# [ E ] 01 String [题解]
  • # 数论-逆元
  • %check_box% in rails :coditions={:has_many , :through}
  • (Mirage系列之二)VMware Horizon Mirage的经典用户用例及真实案例分析
  • (附源码)计算机毕业设计SSM保险客户管理系统
  • (更新)A股上市公司华证ESG评级得分稳健性校验ESG得分年均值中位数(2009-2023年.12)
  • (转)visual stdio 书签功能介绍
  • .NET BackgroundWorker
  • .NET Core 和 .NET Framework 中的 MEF2
  • .NET CORE使用Redis分布式锁续命(续期)问题
  • .NET delegate 委托 、 Event 事件,接口回调
  • .NET Framework .NET Core与 .NET 的区别
  • .Net Redis的秒杀Dome和异步执行
  • .NET/C# 中你可以在代码中写多个 Main 函数,然后按需要随时切换
  • .NET版Word处理控件Aspose.words功能演示:在ASP.NET MVC中创建MS Word编辑器
  • .net快速开发框架源码分享
  • .pub是什么文件_Rust 模块和文件 - 「译」
  • /etc/sudoer文件配置简析
  • [ 隧道技术 ] cpolar 工具详解之将内网端口映射到公网
  • [1]-基于图搜索的路径规划基础
  • [Android开源]EasySharedPreferences:优雅的进行SharedPreferences数据存储操作