WEB开发网      婵犵數濮烽弫鍛婄箾閳ь剚绻涙担鍐叉搐绾剧懓鈹戦悩瀹犲闁汇倗鍋撻妵鍕箛閸洘顎嶉梺绋款儑閸犳劙濡甸崟顖氬唨闁靛ě浣插亾閹烘鈷掗柛鏇ㄥ亜椤忣參鏌″畝瀣暠閾伙絽銆掑鐓庣仭缁楁垿姊绘担绛嬪殭婵﹫绠撻、姘愁樄婵犫偓娴g硶鏀介柣妯款嚋瀹搞儱螖閻樺弶鍟炵紒鍌氱Ч瀹曟粏顦寸痪鎯с偢瀵爼宕煎☉妯侯瀳缂備焦顨嗗畝鎼佸蓟閻旈鏆嬮柣妤€鐗嗗▓妤呮⒑鐠団€虫灀闁哄懐濮撮悾鐤亹閹烘繃鏅濋梺闈涚墕濡瑩顢欒箛鏃傜瘈闁汇垽娼ф禒锕傛煕閵娿儳鍩f鐐村姍楠炴﹢顢欓懖鈺嬬幢闂備浇顫夊畷妯肩矓椤旇¥浜归柟鐑樻尭娴滃綊姊虹紒妯虹仸闁挎洍鏅涜灋闁告洦鍨遍埛鎴︽煙閼测晛浠滃┑鈥炽偢閹鈽夐幒鎾寸彇缂備緡鍠栭鍛搭敇閸忕厧绶炴俊顖滅帛濞呭洭姊绘担鐟邦嚋缂佽鍊垮缁樼節閸ャ劍娅囬梺绋挎湰缁嬫捇宕㈤悽鍛婄厽閹兼番鍨婚埊鏇㈡煥濮樿埖鐓熼煫鍥ュ劤缁嬭崵绱掔紒妯肩畺缂佺粯绻堝畷姗€濡歌缁辨繈姊绘担绛嬪殐闁搞劋鍗冲畷顖炲级閹寸姵娈鹃梺缁樻⒒閳峰牓寮崒鐐寸厱闁抽敮鍋撻柡鍛懅濡叉劕螣鐞涒剝鏂€闂佺粯鍔曞Ο濠囧吹閻斿皝鏀芥い鏃囨閸斻倝鎽堕悙鐑樼厱闁哄洢鍔屾晶顖炴煕濞嗗繒绠婚柡灞界Ч瀹曨偊宕熼鈧▍锝囩磽娴f彃浜炬繝銏f硾椤戝洨绮绘ィ鍐╃厵閻庢稒岣跨粻姗€鏌ㄥ☉妯夹fい銊e劦閹瑩顢旈崟顓濈礄闂備浇顕栭崰鏍礊婵犲倻鏆﹂柟顖炲亰濡茶鈹戦埄鍐ㄧ祷妞ゎ厾鍏樺璇测槈閵忕姈鈺呮煏婢跺牆鍔撮柛鏂款槺缁辨挻鎷呯粙搴撳亾閸濄儳鐭撶憸鐗堝笒閺嬩線鏌熼崜褏甯涢柡鍛倐閺屻劑鎮ら崒娑橆伓 ---闂傚倸鍊搁崐鐑芥倿閿旈敮鍋撶粭娑樺幘濞差亜鐓涢柛娑卞幘椤斿棝姊虹捄銊ユ珢闁瑰嚖鎷�
开发学院软件开发Delphi 转载:TThread类剖析 阅读

转载:TThread类剖析

 2006-02-04 13:39:03 来源:WEB开发网 闂傚倸鍊搁崐椋庢濮橆兗缂氱憸宥堢亱闂佸湱铏庨崰鏍不椤栫偞鐓ラ柣鏇炲€圭€氾拷闂傚倸鍊搁崐椋庣矆娓氣偓楠炲鏁撻悩鎻掔€梺姹囧灩閻忔艾鐣烽弻銉︾厵闁规鍠栭。濂告煕鎼达紕校闁靛洤瀚伴獮鎺楀箣濠靛啫浜鹃柣銏⑶圭壕濠氭煙閻愵剚鐏辨俊鎻掔墛缁绘盯宕卞Δ鍐冣剝绻涘畝濠佺敖缂佽鲸鎹囧畷鎺戭潩閹典焦鐎搁梻浣烘嚀閸ゆ牠骞忛敓锟�婵犵數濮烽弫鍛婃叏椤撱垹绠柛鎰靛枛瀹告繃銇勯幘瀵哥畼闁硅娲熷缁樼瑹閳ь剙岣胯鐓ら柕鍫濇偪濞差亜惟闁宠桨鑳堕崝锕€顪冮妶鍡楃瑐闁煎啿鐖奸崺濠囧即閵忥紕鍘梺鎼炲劗閺呮稒绂掕缁辨帗娼忛埡浣锋闂佽桨鐒﹂幑鍥极閹剧粯鏅搁柨鐕傛嫹闂傚倸鍊搁崐椋庢濮橆兗缂氱憸宥堢亱闂佸湱铏庨崰鏍不椤栫偞鐓ラ柣鏇炲€圭€氾拷  闂傚倸鍊搁崐鐑芥嚄閼哥數浠氱紓鍌欒兌缁垶銆冮崨鏉戠厺鐎广儱顦崡鎶芥煏韫囨洖校闁诲寒鍓熷铏圭磼濡搫顫岄梺鍦拡閸嬪棝鎯€椤忓浂妯勯梺鍝勬湰濞叉ḿ鎹㈠┑濠勭杸闁哄洨濮烽悰銉╂⒒娴e搫甯跺鐟帮攻缁傚秴饪伴崼姘e亾閺冨牆绀冩い蹇庣娴滈箖鏌ㄥ┑鍡涱€楀褜鍠栭湁闁绘ɑ鐟ョ€氼喚绮绘ィ鍐╃厱妞ゆ劑鍊曢弸搴ㄦ煟韫囧鍔滈柕鍥у瀵潙螣閸濆嫬袝婵$偑鍊戦崹娲偡閳哄懎绠栭柍鈺佸暞閸庣喖鏌曢崶褍绨婚柟鍑ゆ嫹
核心提示:TThread类剖析 摘要 本文从分析源代码的角度介绍Delphi5中的TThread类的封装和运行机理,介绍了TThread类的优缺点,转载:TThread类剖析, 关键词:Delphi5,TThread,:) 5.参考文献 1.《Windows核心编程》,机械工业出版社,Windows API 目录 1.概述 2
TThread类剖析

摘要

本文从分析源代码的角度介绍Delphi5中的TThread类的封装和运行机理,介绍了TThread类的优缺点。

关键词:Delphi5,TThread,Windows API

 

目录

1.概述

2.剖析TThread类

2.1 TThread的优点

2.2 TThread的封装和运行机理

3.结束语

4.致谢

5.参考文献

全文

1.概述

根据Windows SDK文档的说明,在Windows线程中的运行实体是类型为:function ThreadFunc(Parameter: pointer): integer的函数(翻译成Delphi的格式)。但是我们都知道,在Delphi中线程被封装成一个TThread类。为什么Delphi要将它封装成一个类?Delphi是如何封装的呢?我们怎样才能充分的利用两者的优点?这就是本下面要介绍的。

2.剖析TThread类

2.1 TThread的优点

将线程作为类来封装有着许多优点。首先它能清晰、安全的界限线程相关的局部变量和进程相关的全局变量。类——对象的模型到实体的映射关系保证了声明在类中的任何变量都是局部的,声明在类外的任何变量都是全局的。所以在写新线程的Execute函数只要注意对类外部的变量、方法的访问就可以了,至于类内部的变量、方法则可以任意使用而不用考虑同步的问题。将线程封装成类的更重要的好处是写新线程的时候可以充分利用类的优点。你可以通过继承来重用父类的功能,这实在是一个激动人心的功能。

2.2 TThread的封装和运行机理

既然已经知道将线程封装成类有诸多好处,作为一个称职的程序员一定会去了解Delphi是如何将线程封装成类的,有没有更好的封装的方法的。

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;


准确地说,TThread对象是一个带有线程实例的不可见窗体对象(长宽都为0),我把这个窗体叫做线程窗体。这个线程窗体有该TThread类的所有对象共享。TThread在构造的时候线程是否第一次创建,如果是就创建线程窗体,然后增加线程计数,最后才建立线程实例。同理,TThread对象在销毁的时候,先减少线程计数,然后判断计数是否为0,如果是就销毁线程窗体。

为什么要建立一个线程窗体呢?答案就是TThread中的同步函数Synchronize()的需要。线程对象存取其他VCL的属性时与其他线程的同步机制是通过消息队列来实现的。当线程函数执行Synchronize()时,他就向线程窗体发送一条CM_EXECPROC消息。因为线程窗体是进程的一个窗体(虽然它不可见),所以发向线程窗体的消息都会进入进程消息队列,而消息队列的串行处理的特性保证不会出现访问冲突。这是一个简单而有效的解决方案。我不知道有没有人在控制台程序中应用多线程,如果有的话,TThread类可能就不太适合了。这种情况下要么直接应用线程函数,要么自己写一个新的TNewThread类了。

Delphi是在TThread类的外面声明了一个局部函数ThreadProc。这个函数就是Windows SDK中介绍的线程函数,其声明如下:

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没有将线程函数作为TThread的一个成员函数,我想把ThreadProc放到TThread的Proctected段中TThread的灵活性可能会更好一点,不过现在的方法也不错。可以看到ThreadProc以TThread对象作为Parameter参数。这样可以保证TThread对象进入线程的堆栈中,一个TThread对象不破坏另一个同类型TThread对象的数据。当然,创建线程的线程还是可以访问新线程中的数据的,Terminate过程就是这样做的。所以TThread的数据还是可能被其他线程破坏的。所以外部线程要访问线程的数据要小心处理,Terminate()是一个比较典型的:外部线程只写,内部线程只读就能很好的工作,如果两个线程都又读又写就可能导致逻辑混乱。

TThread类在构造线程实例是没有直接调用CreateThread() API函数,而是使用了一个BeginThread()函数。不知是什么原因,该函数并没有相应的Delphi Help文档,只是在“TThreadFunc type”的介绍中一笔带过。可能是Borland认为它的参数在以后还会修改吧。不过该函数和CreateThread() API的参数是一模一样的。这是一个让人兴奋的地方,因为BeginThread()加入了Windows API没有的异常处理功能。有意思的是,Delphi在BeginThread()由创建了一个新的线程函数,而把原来的线程函数和参数打包成TThreadRec作为新函数的Parameter。有关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的异常机制
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 //调用原来的线程函数

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;


让人觉得美中不足的地方是TThread类在调用BeginThread时传递的SercurityAttributes和StackSize参数分别是nil和0,使BeginThread()在调用CreateThread()时使用了缺省的安全设置和默认堆栈大小。有关这两个参数代表什么意义请查阅Windows SDK文档。

3.结束语

由于时间仓促,简单介绍我认为Delphi的帮助文档中没有说明的部分。不知你看后有什么疑惑或是觉得我什么讲的不对的地方,请来信告知: zg@hzhistar.com 。请多多指教!

4.致谢

"其实,你这篇文章只适用于Delphi5,Delphi6已经改变了Synchronize的做法,改用事件(Event)和临界区(CriticalSection)的配合来进行同步多线程对VCL控件的访问。其它还有些少改动的地方,相信你看源码就会发现。
另外,(或许你已经知道了)Delphi的文档也很清楚地说明了,调用BeginThread和EndThread来替代Win32API的CreateThread和ExitThread(其实《Windows 核心编程》也指出了应使用开发环境提供的_beginthreadex等函数,具体原因看书吧),至于Delphi,调用BeginThread的一个非常重要的作用就是将全局变量IsMultiThread设为True,因为Delphi的许多运行机制是当该变量为True时才是线程安全的,例如GetMem和FreeMem函数。"
——摘自 "hgd" <hgd01@263.net>

上述的朋友给了我严谨的批评和暖和的鼓舞,我在这里表示衷心的感谢!如果你给我提供了你的想法,我就在这里写上你的大名。:)

5.参考文献

1.《Windows核心编程》,机械工业出版社,2000年5月(虽然中文翻译奇烂)
2.《Microsoft Platform SDK》,microsoft, 2001年8月
3.《Delphi 5.0 帮助文档》、Delphi 5.0源代码

Tags:转载 TThread 剖析

编辑录入:爽爽 [复制链接] [打 印]
赞助商链接