zoukankan      html  css  js  c++  java
  • DELPHI中千万别直接使用CreateThread ,建议使用BeginThread(在C++中无大问题,可是到了DELPHI中情况就不一样了)

    以前在写个别程序的时候老是喜欢使用纯API编程。 
    在C++中无大问题,可是到了DELPHI中情况就不一样了。 
    当你用 DELPHI写的多线程程序莫名其妙的内存错误,特别是字符串(string)操作; 
    或者程序无故终止,又没有任何提示,你需要认真分析可能是你直接使用了CreateThread。 

    C++的linker可以自己设置运行库的形式,选择支持单线程还是多线程模式。 
    DELPHI是自动判别的,那他是如何自动判别的呢,这就要看看他在System单元提供的函数BeginThread了。 听说在VC 中也不赞成直接使用没有保护的
    CreateThread ,也要使用加了保护的_BeginThread。


    {$IFDEF MSWINDOWS} 
    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;”这句了吗? 
    找到他的定义,在全局变量里: 
    IsMultiThread: Boolean;   { True if more than one thread } 

    再看看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} 
            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; 

    这里DELPHI帮你设置了线程的 SEH 处理函数。 
    在DELPHI里,我们应该使用BeginThread,丢掉CreateThread吧。

    *****************************************************************************

    博主:在实际应用中出现了问题

    function GetGuiyue(ABuffer: PArrayByte): Boolean; stdcall; external 'Guiyue.dll';
    我调用函数GetGuiyue时出现异常,

        BeginThread(nil,0,@GetGuiyue,tempBuffer,0,ThreadID); 

    原因是BeginThread 访问函数,与stdcall 接口冲突。所以需要在调用时写一个引用函数

    function ParseGuiyue(ABuffer: PArrayByte): Boolean;  
    begin
      GetGuiyue(ABuffer);  //调用DLL 里的函数
      EndThread(0);          //函数结束关闭线程
    end;

    这样,你就可以放心使用 BeginThread了。

         BeginThread(nil,0,@ParseGuiyue,tempBuffer,0,ThreadID);  //创建子线程处理解析

    但是,也有人提出 BeginThread 使用不安全

    把ParseGuiyue作为BeginThread的参数有两个问题 
      1.P参数无效(ParseGuiyue会从栈顶获取,而实际上在EAX中传递过来) 
      2.函数无法正确返回(ParseGuiyue把栈顶的返回地址当成P参数了,而取了下一个不确定的元素作为返回地址) 
    所以在MyThreadFunc中加EndThread只是让线程在函数返回前结束执行,并不能解决第一个问题——而这可能会带来严重的错误,因为ParseGuiyue里P参数是一个指向代码段内存的地址(ThreadWrapper函数的执行体中某位置)。 
      另外看起来调用EndThread会造成BeginThread中分配的PThreadRec内存泄漏。

    http://www.cnblogs.com/wxy8/archive/2011/06/21/2085661.html

  • 相关阅读:
    性能相关记录
    邮箱SMTP
    IntelliJ IDEA 14.1.7 安装激活
    pychram永久激活
    电脑的奇葩坑
    jmeter过程中的坑
    各数据库驱动连接配置
    spring定时器--时间设置规则
    linux redhat7.2下vim的安装与使用方法
    分数化小数
  • 原文地址:https://www.cnblogs.com/findumars/p/8207016.html
Copyright © 2011-2022 走看看