首 页 网络编程
网页制作 图形图象 操作系统 冲浪宝典
软件教学 认证考试

网络安全 网络办公 行业资讯 评测对比
您当前位置:站长天空 -> 认证考试-> Adobe认证
剖析tthread类_delphi教程
作者:网友供稿 点击:0
推荐
西部数码-全国虚拟主机10强!20余项虚拟主机管理功能,全国领先!第6代双线路虚拟主机,南北访问畅通无阻!可在线rar解压,自动数据恢复设置虚拟目录等.免费赠送访问统计,企业邮局.Cn域名注册10元/年,自助建站480元起,免费试用7天,满意再付款!P4主机租用799元/月.月付免压金
站内搜索
文章页数:[1] 
 

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源代码



文章整理:站长天空 网址:http://www.z6688.com/
以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢!

文章页数:[1] 


放大字体显示 缩小字体显示 打印文章 推荐给朋友
热门文章
·Java开发工具配置 UltraEdit-JSP教程,Java技巧及代码
·遍历设备管理器的设备-.NET教程,评论及其它
·用正则表达式剔除文本中的HTML标记-ASP教程,正则表达式
·一个通用的DataGridTableStyle的做法-.NET教程,数据库应用
·java连接Oracle数据库-JSP教程,Java技巧及代码
·将XML存入关系数据库-JSP教程,数据库相关
·如何在Web页面上直接打开、编辑、创建Office文档-ASP教程,ASP应用
·asp之日期和时间函数示例-ASP教程,ASP应用
·ASP.Net Web Page深入探讨-ASP教程,ASP应用
·浅析Microsoft .net PetShop程序中的购物车和订单处理模块(Profile技术,异步MSMQ消息)-.NET教程,.NET Framework
最新文章
·fireworks 8绘制精致指南针图案_fireworks教程
·卸载多重引导系统中的windows vista操作系统_windows vista
·如何做到google adsense好收入的几点_网赚技巧
·百度主题推广和google adsense的综合比较_网赚技巧
·[新闻会客厅]孙雁:八零后的女闪客_站长访谈
·“流量交换型站点”访客黏度问题凸显_站长心得
·大唐社区站长经验谈社区运营_站长心得
·blog站点如何用rss搜索来推广_站长心得
·自我防护web站点和恶意链接的方法_站长心得
·网站投资你和我的20个自身检查(2)_站长心得
相关主题
西部数码虚拟主机

友情链接
CNNIC 西部数码
万网 自助建站
虚拟主机 asp空间
域名注册 域名
域名申请 主页空间
论坛空间 网站空间
国际域名 虚拟空间
空间租用 DDOS防火墙
成都主机托管 四川主机托管
主机租用 服务器租用
网站目录 自助建站
虚拟主机 网址大全
软件下载
自助链接
虚拟主机资讯 特价虚拟主机
版权申明:本站文章均来自网络,如有侵权,请联系我们,我们收到后立即删除,谢谢!
关于我们:站长天空:专业提供最新的站长资讯、在线教程、虚拟主机权威评测、虚拟主机性能对比、网站制作教程,开发教程,站长工具。包括网页制作教程、冲浪宝典、编程参考、操作系统、软件教学、行业动态等。
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有。
发表评论 打印  刷新     关闭