Delphi 如何解决在DLL的入口函数中创建或结束线程时卡死

先看一下使用Delphi开发DLL时如何使用MAIN函数,

通常情况下并不会使用到DLL的MAIN函数,因为delphi的框架已经把Main函数隐藏起来

而工程函数的 begin  end 默认就是MAIN函数的DLL_PROCESS_ATTACH事件的处理代码,如需要完整的处理其他事件,

如 DLL_PROCESS_DETACH,DLL_THREAD_ATTACH, DLL_THREAD_DETACH,可在工程文件中做如下处理:

procedure DLLEntryPoint(Reason:DWord);
begin
  case Reason of
    DLL_PROCESS_ATTACH:
      StartMyThreadsAndWaitBegin(); // 创建并等待线程开始,这样会导致卡死
    DLL_PROCESS_DETACH:
      StopMyThreadsAndWaitEnd(); // 停止并等待线程结束(或直接结束进程),这样会导致卡死
    DLL_THREAD_ATTACH:;
    DLL_THREAD_DETACH:;
  end;
end;

begin
  DllProc := @DLLEntryPoint;
  DLLEntryPoint(DLL_PROCESS_ATTACH);
end.

其中 DllProc 是SysInit中的全局变量,可简单理解为保存DLL Entry Point入口函数的地址(实际上RTL内部还有InitLib 和StartLib函数,由编译器自动处理)。

以上都是题外话,本文主要说明在DLL入口函数里面创建和退出线程为什么卡死和如何解决的问题。

1)在 DLL_PROCESS_ATTACH 事件中 创建线程 出现卡死的问题

通常情况下在这事件中仅仅是创建并唤醒线程,是不会卡死的,但如果同时有等待线程正式执行的代码,则会卡死,因为在该事件中,任何启动的线程都会由于LdrLoadDll中的LdrpLoaderLock 进入锁定状态而处于等待,无法进入线程函数,所以也就永远无法检测到正式执行的机会。

LdrpLoaderLock是系统的PE Loader的一个重要锁,保证系统资源的安全,而DLL 入口函数是在PE Loader 结束前执行的,LdrInitializeThunk等函数处理PE 映像 到内存中的过程中,LdrpLoaderLock是处于锁定状态的。

所以解决办法就是 在 DLL_PROCESS_ATTACH 事件中,仅创建并唤醒线程即可(此时即使是唤醒了,线程也是处理等待状态),线程函数会在DLL_PROCESS_ATTACH事件结束后才正式执行(实际上如果是通过LoadLibrary加载DLL,则会在LoadLibrary结束前后的某一时刻正式执行)。

2)在DLL_PROCESS_DETACH中结束线程出现卡死的问题

同样的原因,该事件是调用LdrUnloadDll中执行的,LdrpLoaderLock仍然是锁定状态的,而结束线程最终会调用LdrShutdownThread,均会释放PE Loader所维护的系统内部的共同资源(包括PEB 和TEB等模块信息和线程TLS数据等),此类共同资源刚好都是使用LdrpLoaderLock进行同步,所以在DLL_PROCESS_DETACH中调用ExitThread->LdrShutdownThread,必然导致卡死。

另外有一个特殊的现象,就是DLL_PROCESS_DETACH事件中,线程处于挂起状态,这是因为系统分配线程执行时间片的过程中由于PE Loader有资源处于锁定而导致线程无法进行下一个时间片,最终表现为线程函数处于假死状态,此状态基本上等同于线程的挂起(suspend)状态。

解决办法同样是避免在 DLL_PROCESS_DETACH事件中结束线程,那么我们可以在该事件中,创建并唤醒另外一个线程,在该新的线程里,结束需要结束的线程,并在完成后结束自身即可。

总体上代码如下:

procedure DLLEntryPoint(Reason:DWord);
begin
  case Reason of
    DLL_PROCESS_ATTACH:
      TThread.CreateAnonymousThread(procedure begin
        StartMyThreadsAndWaitBegin();
      end).Start;
    DLL_PROCESS_DETACH:
      TThread.CreateAnonymousThread(procedure begin
        StopMyThreadsAndWaitEnd();
      end).Start;
    DLL_THREAD_ATTACH:;
    DLL_THREAD_DETACH:;
  end;
end;

begin
  DllProc := @DLLEntryPoint;
  DLLEntryPoint(DLL_PROCESS_ATTACH);
end.

注: 此问题是属于系统多线程处理的问题,或者说是属于Windows API的使用方法问题,使用其他VB VC等开发的人员也可以参考此解决方法。

时间: 2024-10-12 16:23:46

Delphi 如何解决在DLL的入口函数中创建或结束线程时卡死的相关文章

VS2010中wmain入口函数中使用wprintf输出中文乱码问题

生活中的单例 中国(China),位于东亚,是一个以华夏文明为主体.中华文化为基础,以汉族为主要民族的统一多民族国家,通用汉语.中国疆域内的各个民族统称为中华民族,龙是中华民族的象征.古老的中国凭借自身的发展依旧美丽的屹立于东方民族之林,闪耀着她动人的光彩,世界上只有一个中国,任何部分都是祖国不可分割的一部分,今天我们的设计模式就从伟大的祖国开始说起---单例模式. 详解单例模式 单例模式是什么?跟我们的祖国有着怎样的关系呢?首先我们来看一下单例,从"单例"字面意思上理解为-一个类只有

解决dva dispatch yield生成器函数中异常中断,无法继续调用的问题

在生成器函数中,哪怕是一点报错.都会导致程序无法再次执行.这是yield的特性导致的.最简单的解决方案,就是将所有报错回避,并且做好交互. 1.将所有可能异常的地方判断好,不让代码继续执行yield即可,说白了,就是多判断变量的合法性(undefined.null),以下面的demo为例来说:response就算异常,也中会返回undefined.所以可以根据这个来决定是否还要执行. const response = yield call(loginAdminUser, payload); if

在eclipse中创建maven webapp项目时弹出错误-解决办法

在eclipse中创建maven webapp项目时报错: Could not resolve archetype org.apache.maven.archetypes:maven-archetype-webapp:1.0 from any of the configured repositories. 问题产生原因:是因为本地仓库中缺少了maven-archetype-webapp包,也可能这个包下载不完全,比如:只有pom文件,或只有jar包文件等   [包路径为:C:\Users\xxx

解决“在UBUNTU下打开windows中创建的文本文件,中文显示乱码”的问题 。

在UBUNTU下打开windows中用notepad等工具创建的txt或程序源码等文本文件,中文显示乱码,原因是windows中的txt文件编码方式为GBK,UBUNTU中为utf-8. 解决办法:在终端中使用iconv命令对此文本文件进行转码,使用方法如图所示.具体到我的写法:iconv -f gbk -t utf-8 text.txt -o text.txt.utf8

main入口方法中创建线程执行顺序的问题

1 public static void main(String args[]) { 2 3 4 Thread t1=new Thread(){ 5 public void run(){ 6 System.out.println("1"); 7 } 8 }; 9 Thread t2=new Thread(){ 10 public void run(){ 11 System.out.println("2"); 12 } 13 }; 14 t1.run(); 15 t2

JS入口函数实现-避免覆盖

<!DOCTYPE html><html lang="zh-CN"><head> <meta charset="UTF-8"> <title>JS入口函数实现-避免覆盖</title> <meta name="description" content="author:年少模样"/> <script> //问题:使用两个window.

js入口函数跟jQuery入口函数的区别

JS的window.onload事件必须要等到所有内容,以及外部图片之类的文件加载完之后,才会去执行. JQuery入口函数是在所有标签加载完之后,就会去执行. 接着,通过JS的一个覆盖问题引出对JQuery入口函数实现的解释. JS的入口函数window.onload函数有一个覆盖的问题,当文档中出现2个window.onload函数的时候,后者会覆盖前者,导致功能实现不了.但是JQuery却没有这样的问题,重要是因为JQuery入口函数只是对封装好了的方法的一个调用,只不过传的参数不同而已.

在Delphi中创建线程,请一定使用BeginThread()代替CreateThread()创建线程!(更好的管理异常)

在Delphi中创建线程,请一定使用BeginThread()代替CreateThread()创建线程! 如果直接使用Win32的API函数CreateThread()创建多个线程,也是可以创建的.但是,你应该明白,在每个线程中动态分配和销毁内存块,是需要同步保护的.Delphi语言中有一个在使用多线程环境下至关重要的全局变量IsMultiThread,系统在进行内存分配的时候,根据IsMultiThread变量值判断当前是否使用在多线程环境下,如果该变量为True,哪么,系统在分配和销毁内存的

map函数或reduce函数中如何调用第三方jar包

一般我们在mapreduce程序中调用第三方jar包时会出现找不到jar包的问题,检查发现jar包就在相应路径,mapreduce任务就是找不到.仔细想想会发现,这个jar包是放在执行mapreduce主程序机器上的内存中,一般为客户端机器.而我们在map或者reduce函数中调用该jar包时是在集群的机器上的内存中调用,这样怎么可以调用.可以使用以下方法: 1 把jar包提前放在集群每天机器上. 2 和集群调用mysql驱动程序一样,先将jar包放入hdfs,然后通过mysql的distrib