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

zyb2016 2021-08-13 原文


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

在Delphi中创建线程,请一定使用BeginThread()代替CreateThread()创建线程!
  如果直接使用Win32的API函数CreateThread()创建多个线程,也是可以创建的。但是,你应该明白,在每个线程中动态分配和销毁内存块,是需要同步保护的。Delphi语言中有一个在使用多线程环境下至关重要的全局变量IsMultiThread,系统在进行内存分配的时候,根据IsMultiThread变量值判断当前是否使用在多线程环境下,如果该变量为True,哪么,系统在分配和销毁内存的时候,是要进行同步保护的。相反,则不用同步保护。
  所以,如果你直接使用CreateThread()创建线程,一定要记得手动将IsMultiThread变量置为True。要不然你就会经常发现系统内存非法访问错误!如果你使用Delphi中的BeginThread()创建线程,这个BeginThread()的实现如下:
function BeginThread(SecurityAttributes: Pointer; StackSize: LongWord;
  ThreadFunc: TThreadFunc; Parameter: Pointer; CreationFlags: LongWord;
  var ThreadId: LongWord): Integer;
var
  P: PThreadRec;
begin
  New(P);
  P.Func := ThreadFunc;
  P.Parameter := Parameter;
  IsMultiThread := TRUE;
  Result := CreateThread(SecurityAttributes, StackSize, @ThreadWrapper, P,
    CreationFlags, ThreadID);
end;
  看见了吗?上面已经将IsMultiThread := TRUE;这样就保证了在多线程下,内存使用的安全。
  使用BeginThread()函数替代CreateThread()的另一个重要理由就在上面的的代码段中,你发现了吗?BeginThread()函数内部在调用CreateThread()的时候,哪个线程函数指针用的是@ThreadWrapper,参数用的是P,而 P: PThreadRec;  是一个结构体指针,结构体内部分别才是线程函数和线程函数参数。
  ThreadWrapper函数实现如下:
{$IFDEF MSWINDOWS}
function ThreadWrapper(Parameter: Pointer): Integer; stdcall;
{$ELSE}
function ThreadWrapper(Parameter: Pointer): Pointer; cdecl;
{$ENDIF}
asm
{$IFDEF PC_MAPPED_EXCEPTIONS}
        { Mark the top of the stack with a signature }
        PUSH    UNWINDFI_TOPOFSTACK
{$ENDIF}
        CALL    _FpuInit
        PUSH    EBP
{$IFNDEF PC_MAPPED_EXCEPTIONS}
        XOR     ECX,ECX
        PUSH    offset _ExceptionHandler
        MOV     EDX,FS:[ECX]
        PUSH    EDX
        MOV     FS:[ECX],ESP
{$ENDIF}
{$IFDEF PC_MAPPED_EXCEPTIONS}
    // The signal handling code in SysUtils depends on being able to
    // discriminate between Delphi threads and foreign threads in order
    // to choose the disposition of certain signals.  It does this by
    // testing a TLS index.  However, we allocate TLS in a lazy fashion,
    // so this test can fail unless we\'ve already allocated the TLS segment.
    // So we force the allocation of the TLS index value by touching a TLS
    // value here.  So don\'t remove this silly call to AreOSExceptionsBlocked.
        CALL    AreOSExceptionsBlocked
{$ENDIF}
        MOV     EAX,Parameter
        MOV     ECX,[EAX].TThreadRec.Parameter
        MOV     EDX,[EAX].TThreadRec.Func
        PUSH    ECX
        PUSH    EDX
        CALL    _FreeMem
        POP     EDX
        POP     EAX
        CALL    EDX

{$IFNDEF PC_MAPPED_EXCEPTIONS}
        XOR     EDX,EDX
        POP     ECX
        MOV     FS:[EDX],ECX
        POP     ECX
{$ENDIF}
        POP     EBP
{$IFDEF PC_MAPPED_EXCEPTIONS}
        { Ditch our TOS marker }
        ADD     ESP, 4
{$ENDIF}
end;

  ThreadWrapper函数是使用BASM汇编代码实现,如果你熟悉BASM汇编,你就知道,前面的代码,是BASM汇编中函数传参数的约定,后面的才是重点,它内部实现了当执行线程函数出现异常错误时,转化为异常的机制。

  如果你直接使用CreateThread()创建线程,当然就不会有执行线程函数提供触发异常这样的好处,这才是使用BeginThread()函数替代CreateThread()的最重要原因,也是根本原因。

  线程中的异常机制很重要吗?当然重要!因为线程函数只是一个执行函数过程体,当在这个函数的执行过程中,如果发生异常,程序就会退出当前线程函数,也就是线程会终止。所以在线程中提供异常机制,并让我们在线程中可以捕获处理,是非常至关重要的。

转自http://blog.csdn.net/zang141588761/article/details/51654748

 

发表于
2019-07-08 15:49 
zyb2016 
阅读(316
评论(0
编辑 
收藏 
举报

 

版权声明:本文为zyb2016原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/zyb2016/p/11151686.html

在Delphi中创建线程,请一定使用BeginThread()代替CreateThread()创建线程!(更好的管理异常) - zyb2016的更多相关文章

  1. 七款实用的视图模型制作工具 – 非池中鱼

    七款实用的视图模型制作工具 你是不是常常苦于找不到合适的建模工具来制作你的视图模型呢?或者是制作出来的效果与你 […]...

  2. linux系统下查看某文件修改的时间戳 – kittychentao

    linux系统下查看某文件修改的时间戳   Linux系统下查看某文件修改的时间戳查看文件时间戳命令:stat […]...

  3. html打造动画【系列2】- 可爱的蛙蛙表情 – 狙击手+

    html打造动画【系列2】- 可爱的蛙蛙表情 可爱的小青蛙,一定想不到 先感受一下全部表情包: 在开始之前先安 […]...

  4. 企业的数据管理战略 – FocusBI学院

    企业的数据管理战略 微信公众号:FocusBI 关注可了解更多的商业智能、数据仓库、数据库开发、爬虫知识及沪深 […]...

  5. SCI/EI/ISTP检索、收录查询 – 新西内几米

    SCI/EI/ISTP检索、收录查询 2015-08-24 11:58  新西内几米  阅读(1498)  评 […]...

  6. IP广播地址 – albert1017

    IP广播地址 IP广播地址有四种类型:     受限广播–有限广播的地址设为255.255.255 […]...

  7. 微信服务号、订阅号和企业号的区别(运营和开发两个角度) – 张大大123

    微信服务号、订阅号和企业号的区别(运营和开发两个角度) 一、运营的角度 1.1、概括 订阅号: 微信最初的形态 […]...

  8. 怎么获取红米6 Pro的root权限 – 吓了一跳哦

    怎么获取红米6 Pro的root权限 红米6 Pro能有啥方法获得ROOT超级权限?做开发的人知道,andro […]...

随机推荐

  1. JAVA 8学习笔记-第三章

    CHAPTER 3  CORE JAVA APIs API: Application Programming […]...

  2. php+Apache2+Nginx+Mysql

        Nginx         1.安装Nginx     sudo apt-get clean     […]...

  3. 简单理解Java的反射

    反射(reflect):   JAVA反射机制是在运行状态中,对于任意一个实体类,都能够知道这个类的所有属性和 […]...

  4. 深度学习之损失函数

    机器学习中的所有算法都依靠最小化或最大化函数,我们将其称为“目标函数”。被最小化的函数就被称为“损失函数”。损 […]...

  5. Python 并发总结,多线程,多进程,异步IO

    1 测量函数运行时间 import time def profile(func): def wrapper(* […]...

  6. 【Contest】Nowcoder 假日团队赛1 题解+赛后总结

    比赛链接 通过顺序:\(B\rightarrow D\rightarrow I\rightarrow J\ri […]...

  7. Kubernetes CNI网络插件

    kubeadm安装的集群为什么是cni0的网桥,为什么有些人是docker0网桥。CNI网络插件是什么意思,谁 […]...

  8. 任意文件夹打开CMD命令窗口

    1:打开任意文件夹 2:按住Shift键,鼠标右键单击 3:该文件夹下的命令窗口已打开,方便快捷...

展开目录

目录导航