[轉]DELPHI中千萬別直接使用CreateThread ,建議使用BeginThread

來源:互聯網
上載者:User

標籤:

 
當你用 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記憶體流失。

 

[轉]DELPHI中千萬別直接使用CreateThread ,建議使用BeginThread

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.