线程天敌TerminateThread与SuspendThread http://blog.csdn.net/magictong/article/details/6304439

article/2025/10/28 19:03:35

线程天敌TerminateThread与SuspendThread

标签:  thread null delete dll c user
2011-04-06 13:22  10295人阅读  评论(1)  收藏  举报
 分类:

版权声明:本文为博主原创文章,未经博主允许不得转载。

线程天敌TerminateThread与SuspendThread

 

作者:童磊(magictong)

 

目的:不是演示TerminateThread和SuspendThread的原理而是希望能在自己的程序中摒弃它们。

 

一、不使用TerminateThread的N条理由(先YY一下)

1、如果使用TerminateThread,那么在拥有线程的进程终止运行之前,系统不会撤销该线程的执行堆栈。原因是:如果其它正在执行的线程去引用被强制撤销的线程的堆栈上的值,那么其它的线程就会出现访问违规的问题。

 

2、如果线程正在进行堆分配时被强行撤销,可能会破坏堆,造成堆锁没有释放,如果这时其他线程进行堆分配就会陷入死锁。

 

3、来之MSDN的血淋淋的警告:

a. If the target thread owns a critical section, the critical section will not be released.(如果目标线程持有着一个临界区(critical section),这临界区将不会被释放。)

b. If the target thread is allocating memory from the heap, the heap lock will not be released. (如果目标线程正在堆里分配内存,堆锁(heap lock)将不会被释放。)

c. If the target thread is executing certain kernel32 calls when it is terminated, the kernel32 state for the thread's process could be inconsistent. (如果目标线程在结束时调用了某些kernel32,会造成kernel32的状态不一致。)

d. If the target thread is manipulating the global state of a shared DLL, the state of the DLL could be destroyed, affecting other users of the DLL. (如果目标线程正在更改一个共享DLL的全局状态,这个共享DLL的状态可能会被破坏,影响到其他的正在使用这个DLL库的线程。

 

4、看来,TerminateThread作为终止一个线程的最后的杀招,微软也是不建议大家使用的。但是TerminateThread完全无用么?一般说来确实如此,但是如果你非常清楚你的线程在干什么,并且能够通过代码使线程在TerminateThread的时候优雅的结束,那么你可以用,但是你真的清楚么?在一个大型的工程项目中,试想,拥有10个线程、3个临界区、3个事件触发器的程序中,开发者能清楚线程在任何时刻都在干嘛么?

 

5、也许结束一个线程最可靠的方法就是确定这个线程不休眠无限期的等待下去。一个支持可以被要求停止的线程,它必须定期的检查看它是否被要求停止或者如果它在休眠的话看它是否能够被唤醒。支持这两个情况最简单的的方法就是使用同步对象,这样应用程序的主线程和工作中的线程能互相沟通。当应用程序希望关闭线程时,只要设置这个同步对象,等待线程退出。之后,线程把这个同步对象作为它周期性监测的一部分,定期检查,或者如果这个同步对象的状态改变的话,就可以执行清理操作并退出了。

 

二、简单的演示例子

TerminateThread:

void CThreadDeadlocksDlg::OnBnClickedBtnTerminatethread()

{

         HANDLE hThread = ::CreateThread(NULL, 0, TerminateThreadHandle, NULL, 0, NULL);

 

         Sleep(1000);

 

         ::TerminateThread(hThread, 0);

         hThread = NULL;

 

         char* p = new char[4096];

         delete p;

 

         return;

}

// 一个循环分配内存的线程

DWORD __stdcall CThreadDeadlocksDlg::TerminateThreadHandle(void* )

{

         while(1)

         {

                   char* p = new char[4096];

                   delete p;

         }

}

 

看一下callstack和locks情况,就比较清楚了。

0:000> !locks

 

CritSec ntdll!LdrpLoaderLock+0 at 7c99e178

LockCount          0

RecursionCount     1

OwningThread       55c

EntryCount         1

ContentionCount    1

*** Locked

 

CritSec +3b0608 at 003b0608

LockCount          2

RecursionCount     0

OwningThread       0

EntryCount         2

ContentionCount    2

*** Locked

 

0:000> ~* kb

 

.  0  Id: 1540.12ac Suspend: 1 Teb: 7ffdf000 Unfrozen

ChildEBP RetAddr  Args to Child             

0012f57c 7c92df5a 7c93ac7b 00000110 00000000 ntdll!KiFastSystemCallRet

0012f580 7c93ac7b 00000110 00000000 00000000 ntdll!ZwWaitForSingleObject+0xc

0012f608 7c921046 003b0608 7c930dd0 003b0608 ntdll!RtlpWaitForCriticalSection+0x132

0012f610 7c930dd0 003b0608 00001000 00000000 ntdll!RtlEnterCriticalSection+0x46

0012f83c 78134d83 003b0000 00000000 00001000 ntdll!RtlAllocateHeap+0x2f0

0012f85c 783095c4 00001000 0012fe90 00000114 MSVCR80!malloc+0x7a [f:/dd/vctools/crt_bld/self_x86/crt/src/malloc.c @ 163]

 

SuspendThread也有同样的问题(SuspendThread本身也是比较暴力的):

http://blog.csdn.net/magictong/archive/2009/05/08/4161571.aspx这里讲解了一个使用SuspendThread造成主线程LoadLibrary里面hang的例子。

void CThreadDeadlocksDlg::OnBnClickedBtnSuspendthread()

{

         HANDLE hThread = ::CreateThread(NULL, 0, SuspendThreadHandle, NULL, 0, NULL);

 

         Sleep(1000);

 

         ::SuspendThread(hThread);

         hThread = NULL;

 

         char* p = new char[4096];

         delete p;

 

         return;

}

// 一个会被挂起的线程

DWORD __stdcall CThreadDeadlocksDlg::SuspendThreadHandle(void* )

{

         while(1)

         {

                   char* p = new char[4096];

                   delete p;

         }

}

 

0:000> !locks

 

CritSec ntdll!LdrpLoaderLock+0 at 7c99e178

LockCount          0

RecursionCount     1

OwningThread       1fdc

EntryCount         2

ContentionCount    2

*** Locked

 

CritSec +3b0608 at 003b0608

LockCount          2

RecursionCount     1

OwningThread       1844

EntryCount         2

ContentionCount    2

*** Locked

 

.  0  Id: 1a48.1900 Suspend: 1 Teb: 7ffdf000 Unfrozen

ChildEBP RetAddr  Args to Child             

0012f57c 7c92df5a 7c93ac7b 00000108 00000000 ntdll!KiFastSystemCallRet

0012f580 7c93ac7b 00000108 00000000 00000000 ntdll!ZwWaitForSingleObject+0xc

0012f608 7c921046 003b0608 7c930dd0 003b0608 ntdll!RtlpWaitForCriticalSection+0x132

0012f610 7c930dd0 003b0608 00001000 00000000 ntdll!RtlEnterCriticalSection+0x46

0012f83c 78134d83 003b0000 00000000 00001000 ntdll!RtlAllocateHeap+0x2f0

0012f85c 783095c4 00001000 0012fe90 000000f8 MSVCR80!malloc+0x7a

 

   2  Id: 1a48.1844 Suspend: 2 Teb: 7ffdd000 Unfrozen

ChildEBP RetAddr  Args to Child             

012afe88 7c93080b 003b0000 003b8638 012aff40 ntdll!RtlpCoalesceFreeBlocks+0x373

012aff5c 78134c39 003b0000 00000000 003bc750 ntdll!RtlFreeHeap+0x2e9

012affa8 004014b0 003bc750 00001000 7c80b729 MSVCR80!free+0xcd

012affb4 7c80b729 00000000 74680000 003a0043 ThreadDeadlocks!CThreadDeadlocksDlg::SuspendThreadHandle+0x10

012affec 00000000 004014a0 00000000 00000000 kernel32!BaseThreadStart+0x37

 

三、常驻进程里面遇到的问题

         当然实际coding的过程不会像上面的代码那么暴力,但是就不会有问题了吗?当然不是的,只是碰到的概率降低了而已,问题依然存在(没有PDB了,杯具)。

第一个dmp:

.  0  Id: e50.a20 Suspend: 1 Teb: 7ffdf000 Unfrozen

ChildEBP RetAddr  Args to Child             

WARNING: Stack unwind information not available. Following frames may be wrong.

0012fc08 77962148 00000000 00000000 10000000 ntdll!KiFastSystemCallRet

0012fc30 7798c908 77a07340 7793cf13 0012fdb0 ntdll!EtwEventEnabled+0xd9

0012fc70 75b588bc 10000000 00000000 0012fd60 ntdll!LdrUnloadDll+0x2a

0012fc80 0041dd09 10000000 0041e0b6 00000401 KERNELBASE!FreeLibrary+0x82

0012fd60 76a5c5e7 0041d530 00370524 00000401 TSVulFWMan+0x1dd09

0012fdd8 76a54f0e 00000000 0041d530 00370524 USER32!gapfnScSendMessage+0x2cf

0012fe34 76a54f7d 006ffb98 00000401 00000000 USER32!GetScrollBarInfo+0xfd

0012fe5c 77976fee 0012fe74 00000018 0012ff78 USER32!GetScrollBarInfo+0x16c

0012fea8 0041d4eb 0012fecc 00000000 00000000 ntdll!KiUserCallbackDispatcher+0x2e

0012ff88 76233c45 7ffdd000 0012ffd4 779937f5 TSVulFWMan+0x1d4eb

0012ff94 779937f5 7ffdd000 7793ccb7 00000000 kernel32!BaseThreadInitThunk+0x12

0012ffd4 779937c8 00426dfb 7ffdd000 00000000 ntdll!RtlInitializeExceptionChain+0xef

0012ffec 00000000 00426dfb 7ffdd000 00000000 ntdll!RtlInitializeExceptionChain+0xc2

 

         第二个dmp:

   0  Id: f54.3e8 Suspend: 1 Teb: 7ffde000 Unfrozen

ChildEBP RetAddr  Args to Child             

WARNING: Stack unwind information not available. Following frames may be wrong.

0012f750 77962148 00000000 00000000 00220000 ntdll!KiFastSystemCallRet

0012f778 7795fc7f 00220138 7793b612 0000003f ntdll!EtwEventEnabled+0xd9

0012f854 77985ae0 00000041 00000050 002201d4 ntdll!RtlFreeThreadActivationContextStack+0x480

0012f8d8 764c9d45 00220000 00000000 00000041 ntdll!wcsnicmp+0x1e4

0012f8f8 74b7ebb5 00000041 0012fdc0 00000000 msvcrt!malloc+0x57

 

  • 上一篇怎样写一个基本正确的单例模式?
  • 下一篇运行时错误检查(/RTC)编译选项及实现原理
0

http://chatgpt.dhexx.cn/article/1QapgtOx.shtml

相关文章

511遇见易语言API模块线程挂起(SuspendThread)

线程挂起用到的API是SuspendThread 511遇见易语言模块API教程 API 暂停指定的线程 函数功能: 挂起线程。 参数: 第1个参数: HANDLE hThread 线程句柄。 返回值: 成功:线程的前一个挂起数。 失败:-1。 …

面向对象设计6大原则

概览 单一职责 1、单一职责 Single Responsibility Principle,简称是SRP。SRP的英文定义是: There should never be more than one reason for a class to change. 翻译过来的意思是: 应该有且仅有一个原因引起类的变更。 或许我们可以…

带你了解面向对象的设计原则

##一 摘要 今天晚上给大家介绍一下面向对象的设计原则,为什么要介绍这个呢,原因很简单,大家平时所接触的语言,无论是object-C,C,JavaScrpt,C#等都是属于面向对象语言,既然是面向对象设计语言,那么就有必要去了解一下面向对象的设计原则.那么面向对象的设计原则有哪些呢?今天来…

面向对象七大设计原则

面向对象设计的七大原则 前言 在软件开发中,为了提高软件系统的可维护性和可复用性,增加软件的可扩展性和灵活性,我们能要尽量根据 7 条原则来开发程序,从而提高软件开发效率、节约软件开发成本和维护成本。 面向对象设计原则概…

软件设计与体系——面向对象设计的原则

一:前言 用一道题熟悉OO设计与编程:给定一个4位数(数字不完全相同),这4个数字可以得到最大4位数和最小4位数,算它们的差,不是6174则重复上述过程,最终会得到6174,验证这…

面向对象设计七大原则

面向对象设计七大原则 1) 开-闭原则 (Open Closed Principle,OCP)定义开闭原则的作用实现方法例子其它例子代码 2) 里氏代换原则(Liskov Substitutiion Principle,LSP)定义作用实现方法例子 3) 依赖倒置原则…

面向对象设计六大原则

6大原则如下: 1)单一职责原则,一个合理的类,应该仅有一个引起它变化的原因,即单一职责,就是设计的这个类功能应该只有一个; 优点:消除耦合,减小因需求变化引起代码僵化。 2) 开-闭原则,讲的是设计要对扩展…

设计模式(三)——面向对象设计原则

设计模式需要遵循基本的软件设计原则。可维护性(Maintainability)和可复用性(Reusability)是衡量软件质量的重要的两个属性: 可维护性:软件能够被理解、改正、适应及扩展的难易程度可复用性:软件能够被复用的难易程度面向对象设计的原则是支持可维护性复用,一方面需要实…

程序员必备的七大面向对象设计原则(一)

花絮 每天都在和面向对象打交道,但是我们在应用面向对象的时候感觉自己的面向对象技术应用的很合理?理解的很到位?应用的很到位?用的时候恰到好处?用的是否符合软件的发展趋势? 上面很多一连串的问题&#…

面向对象设计原则之开闭原则

开闭原则是面向对象的可复用设计的第一块基石,它是最重要的面向对象设计原则。开闭原则由Bertrand Meyer于1988年提出,其定义如下: 开闭原则(Open-Closed Principle, OCP):一个软件实体应当对扩展开放,对修改关闭。即…

面向对象设计模式5大基本原则

“宇宙万物之中,没有一样东西能像思想那么顽固。” 一爱默生 首先明确模式是针对面向对象的,它的三大特性,封装、继承、多态。 面向对象设计模式有5大基本原则:单一职责原则、开发封闭原则、依赖倒置原则、接口隔离原则、Liskov替换原…

面向对象六大设计原则

目录 1 、单一职责(Single Responsibility Principle) 2 、开闭原则(Open Close Principle) 3、里氏替换原则(Liskov Substitution Principle) 4、接口隔离原则(Interface Segregation Prin…

阿里巴巴编码规范解读(六、七)-工程结构及设计规约

工程结构 应用分层 1.【推荐】 图中默认上层依赖于下层,箭头关系表示可直接依赖,如:开放接口层可以依赖于 Web层,也可以直接依赖于 Service层,依此类推。 开放接口层:可直接封装 Service 方法暴露成RPC…

面向对象设计的七大设计原则详解

面向对象的七大设计原则 文章目录 面向对象的七大设计原则简述七大原则之间的关系 一、开闭原则(The Open-Closed Principle ,OCP)概念理解系统设计需要遵循开闭原则的原因开闭原则的实现方法一个符合开闭原则的设计开闭原则的相对性 二、 里…

Android Binder机制简述

一、Android binder是什么? Android平台上的一种跨进程通信(IPC)机制 从OpenBinder演化而来 从Android应用层角度来说,Binder是客户端和服务端进行通信的媒介 二、IPC原理 每个Android进程,只能运行在自己的进程所…

不得不说的Android Binder机制与AIDL

说起Android的进程间通信,想必大家都会不约而同的想起Android中的Binder机制。而提起Binder,想必也有不少同学会想起初学Android时被Binder和AIDL支配的恐惧感。但是作为一个Android开发者,Binder是我们必须掌握的知识。因为它是构架整个Andr…

FrameWork-进程间通信之Binder机制AIDL

进程间通信之Binder机制 Binder是什么?为什么要使用Binder?Binder原理Binder使用实战 1、Binder是什么 Binder 是一种进程间通信机制,基于开源的 OpenBinder 实现;OpenBinder 起初由 Be Inc. 开发,后由 Plam Inc. 接…

Android 应用是如何启动 binder 机制的?

文章目录 应用是什么时候支持 binder 机制的应用大致启动流程是什么总结问题 应用是什么时候支持 binder 机制的 binder 都知道是用来做跨进程服务的,应用内可以通过binder 跟系统组件进行交互。如 AMS Service aidl 等那么可以思考一下,当 Activity on…

Android Binder机制介绍

转载于:http://www.cnblogs.com/zc9527/p/5638688.html 备注:该博客简单介绍类型的 做过Android开发的同学可能有些体会,入门初期,工作内容主要是实现各式各样的UI界面,以及实现应用的业务逻辑。在这个阶段&#xff0c…

Android跨进程通信Binder机制与AIDL实例

文章目录 进程通信1.1 进程空间划分1.2 跨进程通信IPC1.3 Linux跨进程通信1.4 Android进程通信 Binder跨进程通信2.1 Binder简介2.2 Binder驱动2.3 Binder原理 AIDL编程Demo3.1 服务端3.2 客户端 在操作系统中,进程与进程间的内存和数据都是不共享的。这样做的目的&…