zoukankan      html  css  js  c++  java
  • 在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

  • 相关阅读:
    try catch finally语句块中存在return语句时的运行流程
    【Java学习】异常
    【Java学习】面向对象(二)——封装、继承、多态、抽象
    【ASP.NET开发Web项目】vs2019新建ASP.NET空网站,没有出现Default.aspx
    【ASP.NET开发Web项目】VS2019项目模板中没有ASP.NET空网站
    【Java学习】面向对象(一)
    【Java学习】数组的初始化、特点及基础操作(冒泡排序)
    Jmeter接口测试和压力测试的配置和使用
    【Java学习】递归算法之斐波那契数列、100以内的阶乘
    【Java学习】进制转换、二进制原码反码补码和位运算
  • 原文地址:https://www.cnblogs.com/findumars/p/6711293.html
Copyright © 2011-2022 走看看