国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 學(xué)院 > 開(kāi)發(fā)設(shè)計(jì) > 正文

剖析TThread類

2019-11-18 18:41:56
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友
 

TThread類剖析


摘要


    本文從分析源代碼的角度介紹Delphi5中的TThread類的封裝和運(yùn)行機(jī)理,介紹了TThread類的優(yōu)缺點(diǎn)。


    關(guān)鍵詞:Delphi5,TThread,Windows API


 


目錄


    1.概述


    2.剖析TThread類


        2.1 TThread的優(yōu)點(diǎn)


        2.2 TThread的封裝和運(yùn)行機(jī)理


    3.結(jié)束語(yǔ)


    4.致謝


    5.參考文獻(xiàn)


全文


1.概述


    根據(jù)Windows SDK文檔的說(shuō)明,在Windows線程中的運(yùn)行實(shí)體是類型為:function ThreadFunc(Parameter: pointer): integer的函數(shù)(翻譯成Delphi的格式)。但是我們都知道,在Delphi中線程被封裝成一個(gè)TThread類。為什么Delphi要將它封裝成一個(gè)類?Delphi是如何封裝的呢?我們?cè)鯓硬拍艹浞值睦脙烧叩膬?yōu)點(diǎn)?這就是本下面要介紹的。


2.剖析TThread類


    2.1 TThread的優(yōu)點(diǎn)


    將線程作為類來(lái)封裝有著許多優(yōu)點(diǎn)。首先它能清晰、安全的界限線程相關(guān)的局部變量和進(jìn)程相關(guān)的全局變量。類——對(duì)象的模型到實(shí)體的映射關(guān)系保證了聲明在類中的任何變量都是局部的,聲明在類外的任何變量都是全局的。所以在寫新線程的Execute函數(shù)只要注意對(duì)類外部的變量、方法的訪問(wèn)就可以了,至于類內(nèi)部的變量、方法則可以任意使用而不用考慮同步的問(wèn)題。將線程封裝成類的更重要的好處是寫新線程的時(shí)候可以充分利用類的優(yōu)點(diǎn)。你可以通過(guò)繼承來(lái)重用父類的功能,這實(shí)在是一個(gè)激動(dòng)人心的功能。


    2.2 TThread的封裝和運(yùn)行機(jī)理


    既然已經(jīng)知道將線程封裝成類有諸多好處,作為一個(gè)稱職的程序員一定會(huì)去了解Delphi是如何將線程封裝成類的,有沒(méi)有更好的封裝的方法的。


    Delphi5中TThread類是這樣聲明的:


{ TThread }

EThread = class(Exception);

TThreadMethod = PRocedure of object;
TThreadPriority = (tpIdle, tpLowest, tpLower, tpNormal, tpHigher, tpHighest,
  tpTimeCritical);

TThread = class
private
  FHandle: THandle;
  FThreadID: THandle;
  FTerminated: Boolean;
  FSuspended: Boolean;
  FFreeOnTerminate: Boolean;
  FFinished: Boolean;
  FReturnValue: Integer;
  FOnTerminate: TNotifyEvent;
  FMethod: TThreadMethod;
  FSynchronizeException: TObject;
  procedure CallOnTerminate;
  function GetPriority: TThreadPriority;
  procedure SetPriority(Value: TThreadPriority);
  procedure SetSuspended(Value: Boolean);
protected
  procedure DoTerminate; virtual;
  procedure Execute; virtual; abstract;
  procedure Synchronize(Method: TThreadMethod);
  property ReturnValue: Integer read FReturnValue write FReturnValue;
  property Terminated: Boolean read FTerminated;
public
  constructor Create(CreateSuspended: Boolean);
  destructor Destroy; override;
  procedure Resume;
  procedure Suspend;
  procedure Terminate;
  function WaitFor: LongWord;
  property FreeOnTerminate: Boolean read FFreeOnTerminate write FFreeOnTerminate;
  property Handle: THandle read FHandle;
  property Priority: TThreadPriority read GetPriority write SetPriority;
  property Suspended: Boolean read FSuspended write SetSuspended;
  property ThreadID: THandle read FThreadID;
  property OnTerminate: TNotifyEvent read FOnTerminate write FOnTerminate;
end;


    準(zhǔn)確地說(shuō),TThread對(duì)象是一個(gè)帶有線程實(shí)例的不可見(jiàn)窗體對(duì)象(長(zhǎng)寬都為0),我把這個(gè)窗體叫做線程窗體。這個(gè)線程窗體有該TThread類的所有對(duì)象共享。TThread在構(gòu)造的時(shí)候線程是否第一次創(chuàng)建,如果是就創(chuàng)建線程窗體,然后增加線程計(jì)數(shù),最后才建立線程實(shí)例。同理,TThread對(duì)象在銷毀的時(shí)候,先減少線程計(jì)數(shù),然后判斷計(jì)數(shù)是否為0,如果是就銷毀線程窗體。


    為什么要建立一個(gè)線程窗體呢?答案就是TThread中的同步函數(shù)Synchronize()的需要。線程對(duì)象存取其他VCL的屬性時(shí)與其他線程的同步機(jī)制是通過(guò)消息隊(duì)列來(lái)實(shí)現(xiàn)的。當(dāng)線程函數(shù)執(zhí)行Synchronize()時(shí),他就向線程窗體發(fā)送一條CM_EXECPROC消息。因?yàn)榫€程窗體是進(jìn)程的一個(gè)窗體(雖然它不可見(jiàn)),所以發(fā)向線程窗體的消息都會(huì)進(jìn)入進(jìn)程消息隊(duì)列,而消息隊(duì)列的串行處理的特性保證不會(huì)出現(xiàn)訪問(wèn)沖突。這是一個(gè)簡(jiǎn)單而有效的解決方案。我不知道有沒(méi)有人在控制臺(tái)程序中應(yīng)用多線程,如果有的話,TThread類可能就不太適合了。這種情況下要么直接應(yīng)用線程函數(shù),要么自己寫一個(gè)新的TNewThread類了。


    Delphi是在TThread類的外面聲明了一個(gè)局部函數(shù)ThreadProc。這個(gè)函數(shù)就是Windows SDK中介紹的線程函數(shù),其聲明如下:


function ThreadProc(Thread: TThread): Integer;
var
  FreeThread: Boolean;
begin
  try
    Thread.Execute;
  finally
    FreeThread := Thread.FFreeOnTerminate;
    Result := Thread.FReturnValue;
    Thread.FFinished := True;
    Thread.DoTerminate;
    if FreeThread then Thread.Free;
    EndThread(Result);
  end;
end;


    Delphi沒(méi)有將線程函數(shù)作為TThread的一個(gè)成員函數(shù),我想把ThreadProc放到TThread的Proctected段中TThread的靈活性可能會(huì)更好一點(diǎn),不過(guò)現(xiàn)在的方法也不錯(cuò)。可以看到ThreadProc以TThread對(duì)象作為Parameter參數(shù)。這樣可以保證TThread對(duì)象進(jìn)入線程的堆棧中,一個(gè)TThread對(duì)象不破壞另一個(gè)同類型TThread對(duì)象的數(shù)據(jù)。當(dāng)然,創(chuàng)建線程的線程還是可以訪問(wèn)新線程中的數(shù)據(jù)的,Terminate過(guò)程就是這樣做的。所以TThread的數(shù)據(jù)還是可能被其他線程破壞的。所以外部線程要訪問(wèn)線程的數(shù)據(jù)要小心處理,Terminate()是一個(gè)比較典型的:外部線程只寫,內(nèi)部線程只讀就能很好的工作,如果兩個(gè)線程都又讀又寫就可能導(dǎo)致邏輯混亂。


    TThread類在構(gòu)造線程實(shí)例是沒(méi)有直接調(diào)用CreateThread() API函數(shù),而是使用了一個(gè)BeginThread()函數(shù)。不知是什么原因,該函數(shù)并沒(méi)有相應(yīng)的Delphi Help文檔,只是在“TThreadFunc type”的介紹中一筆帶過(guò)。可能是Borland認(rèn)為它的參數(shù)在以后還會(huì)修改吧。不過(guò)該函數(shù)和CreateThread() API的參數(shù)是一模一樣的。這是一個(gè)讓人興奮的地方,因?yàn)锽eginThread()加入了Windows API沒(méi)有的異常處理功能。有意思的是,Delphi在BeginThread()由創(chuàng)建了一個(gè)新的線程函數(shù),而把原來(lái)的線程函數(shù)和參數(shù)打包成TThreadRec作為新函數(shù)的Parameter。有關(guān)Delphi5中BeginThread的定義如下:


type
  PThreadRec = ^TThreadRec;
  TThreadRec = record
    Func: TThreadFunc;
    Parameter: Pointer;
  end;


function ThreadWrapper(Parameter: Pointer): Integer; stdcall;
asm
  CALL _FpuInit
  XOR ECX,ECX
  PUSH EBP
  PUSH offset _ExceptionHandler  //新增加的Delphi的異常機(jī)制
  MOV EDX,FS:[ECX]
  PUSH EDX
  MOV EAX,Parameter
  MOV FS:[ECX],ESP

  MOV ECX,[EAX].TThreadRec.Parameter
  MOV EDX,[EAX].TThreadRec.Func
  PUSH ECX
  PUSH EDX
  CALL _FreeMem
  POP EDX
  POP EAX
  CALL EDX  //調(diào)用原來(lái)的線程函數(shù)

  XOR EDX,EDX
  POP ECX
  MOV FS:[EDX],ECX
  POP ECX
  POP EBP
end;


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;


    讓人覺(jué)得美中不足的地方是TThread類在調(diào)用BeginThread時(shí)傳遞的SercurityAttributes和StackSize參數(shù)分別是nil和0,使BeginThread()在調(diào)用CreateThread()時(shí)使用了缺省的安全設(shè)置和默認(rèn)堆棧大小。有關(guān)這兩個(gè)參數(shù)代表什么意義請(qǐng)查閱Windows SDK文檔。


3.結(jié)束語(yǔ)


    由于時(shí)間倉(cāng)促,簡(jiǎn)單介紹我認(rèn)為Delphi的幫助文檔中沒(méi)有說(shuō)明的部分。不知你看后有什么疑惑或是覺(jué)得我什么講的不對(duì)的地方,請(qǐng)來(lái)信告知: zg@hzhistar.com 。請(qǐng)多多指教!


4.致謝

    "其實(shí),你這篇文章只適用于Delphi5,Delphi6已經(jīng)改變了Synchronize的做法,改用事件(Event)和臨界區(qū)(CriticalSection)的配合來(lái)進(jìn)行同步多線程對(duì)VCL控件的訪問(wèn)。其它還有些少改動(dòng)的地方,相信你看源碼就會(huì)發(fā)現(xiàn)。
    另外,(或許你已經(jīng)知道了)Delphi的文檔也很清楚地說(shuō)明了,調(diào)用BeginThread和EndThread來(lái)替代Win32API的CreateThread和ExitThread(其實(shí)《Windows 核心編程》也指出了應(yīng)使用開(kāi)發(fā)環(huán)境提供的_beginthreadex等函數(shù),具體原因看書(shū)吧),至于Delphi,調(diào)用BeginThread的一個(gè)非常重要的作用就是將全局變量IsMultiThread設(shè)為True,因?yàn)镈elphi的許多運(yùn)行機(jī)制是當(dāng)該變量為True時(shí)才是線程安全的,例如GetMem和FreeMem函數(shù)。"
——摘自 "hgd" <hgd01@263.net>
 

    上述的朋友給了我嚴(yán)謹(jǐn)?shù)呐u(píng)和暖和的鼓舞,我在這里表示衷心的感謝!如果你給我提供了你的想法,我就在這里寫上你的大名。:)


5.參考文獻(xiàn)


    1.《Windows核心編程》,機(jī)械工業(yè)出版社,2000年5月(雖然中文翻譯奇爛)
    2.《Microsoft Platform SDK》,microsoft, 2001年8月
    3.《Delphi 5.0 幫助文檔》、Delphi 5.0源代碼



上一篇:“98五筆字型輸入法”大批量造詞

下一篇:理解類引用這種類型

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
學(xué)習(xí)交流
熱門圖片

新聞熱點(diǎn)

疑難解答

圖片精選

網(wǎng)友關(guān)注

主站蜘蛛池模板: 额济纳旗| 泰和县| 绥江县| 华亭县| 大同市| 略阳县| 大荔县| 龙泉市| 长治县| 定边县| 凤山市| 兰西县| 万全县| 大埔区| 富平县| 安阳县| 海阳市| 黔江区| 都昌县| 屏东市| 濮阳县| 阜阳市| 华蓥市| 东海县| 蒙阴县| 雅江县| 政和县| 澄城县| 东光县| 固安县| 五原县| 新河县| 海安县| 闻喜县| 灵台县| 建始县| 安新县| 漳浦县| 沂南县| 余庆县| 阿图什市|