南京陆军指挥学院研究生队 李晓峰
Delphi 的VCL(Visual Component Library) 和RTL(Run-Time Library) 库把几乎全部的Windows API 函数封装起来,而且在其基础上增加了一些安全措施。在实际运用时,程序员几乎没有必要直接调用Windows API 函数,利用各单元所提供的函数库或例程库就可直接对系统低层进行操作。
今年8 月10 日,第30 期《计算机世界》G18 版上刊登了一篇《〈在Delphi 中利用线程》的文章,其基本思想就是直接调用Windows API 的CreateThread 函数来创建一个线程。那么,利用Delphi 自身所定义的丰富的标准例程库和数量更多、内容更广泛的(非)可视类库进行处理,以取代直接调用Windows API 函数可否实现呢?回答是肯定的,而且更有效、更保险:
方法一:利用RTL 库的System 单元中定义的一个标准例程BeginThread。此例程完整封装了Win32 的CreateThread 函数,是一个带有异常处理的标准Pascal 函数,几乎可以处理所有自身的异常,相对于使用Win32 的CreateThread 函数,其安全系数大大增强。
BeginThread 函数在创建时,不是如CreateThread 函数仅仅完成两项任务:创建一个线程;创建一个能作为线程入口的函数。还增加了几项保护措施:
把System 单元中声明的全局变量IsMultiThread 设为TRUE,这样Delphi 的堆栈管理器就知道当前有多个线程在运行,从而防止多个线程同时修改它的内部结构;
另外,在调用BeginThread 函数时,可创建一个异常框架,允许系统缺省的异常处理句柄捕获任何未有被处理的异常线程。如果在线程函数中有任何未被处理的异常,会自动产生一个退出代码,或者线程返回的句柄为0,表示线程没有创建成功,则应用程序将会调用EndThread 过程(Procedure EndThread(ExitCiode:Integer)),自动终止线程的运行。
其完整声明如下:
function BeginThread( SecurityAttributes: Pointer;
StackSize: Integer;
ThreadFunc: TThreadFunc;
Parameter: Pointer;
CreationFlags: Integer;
var ThreadId: Integer): Integer;
各参数的使用特点类似CreateThread 函数:
SecurityAttributes 参数是一个指向SECURITY_ATTRIBUTES 结构的指针,其目的用于设置线程的访问权限,nil 表示为默认的安全属性。
StackSize 参数用于设置分配给线程的栈空间大小,0 表示用默认值。
ThreadFunc 用于指定一个函数,该函数在线程创建后开始执行代码时调用。
Parameter 参数传递给ThreadFunc 参数所指定的函数,常为nil,或者设为一个32 位的指针,指向一个数据结构。
CreationFlags 参数用于指定线程创建后是不是立即执行,0 表示立即执行,CREATE_SUSPENDED 表示处于挂起状态。
ThreadId 参数表示为每个线程唯一的识别号,当BeginThread 函数返回后,此参数就是线程的识别号。
返回值为该线程的句柄,如果为0,表示表示线程没有创建成功,可以调用Windows 的GetLastError 函数分析错误的原因。
方法二:利用Delphi 的VCL 库中TThread 对象。Delphi 的一个缺陷是不支持多个线程同时访问它的VCL 库,但Delphi 的设计者们并没有刻意掩饰这个缺陷,而是专门创建了一个TThread 对象以解决这个问题。这个TThread 对象封装了Windows API 和System 单元中有关线程运用的多个函数和例程,利用操作系统分时段给各线程方式控制各个线程的“休眠”与“唤醒”以达到线程工作的同步,当被“唤醒”后就调用TThread 对象的Synchronize 过程通知主线程,让主线程去真正地访问VCL,使得在一个应用程序中同时访问多个VCL 库成为了可能,当然,对于进行一般的多线程编程,就更加简单了。
在使用上它与Delphi 中大多数对象不同的是TThread 类是一个带有虚拟抽象对象方法的类,我们不能直接创建TThread 对象的实例,而必须先声明一个以TThread 对象为基类的类,再用这个派生类创建实例和操纵线程具体的类属性和方法。具体声明可参看其源代码(c:/program files/borland/delphi 3/source/vcl/classes.pas)或其它参考书。
结合上述两种方法给出一例子程序,以显示利用Delphi 自身强大的处理功能如何建立及使用线程,创建一个Form 窗体,Caption 设为“线程创建例子程序”, 在窗体中放入四个TButton 控件,分别设置Caption"例程创建成功”、“例程创建失败”和“类创建成功”、“类创建失败”。Name 分别为“routines1Button"、“routines2Button"和“class1button"、“class2button",一个TEedit 控件,text 设置为“”:
unit threadsimple;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs,
StdCtrls;
type
TForm1 = class(TForm)
Edit1: TEdit;
class1button: TButton;
class2button: TButton;
routines1Button: TButton;
routines2Button: TButton;
procedure class1buttonClick(Sender: TObject);
procedure routines1ButtonClick(Sender: TObject);
procedure routines2ButtonClick(Sender: TObject);
procedure class2buttonClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
tmythread=class(tthread)
count:integer;
myedit:tedit;
procedure show;virtual;abstract;
constructor create(myedit1:tedit);
end;
thread1=class(tmythread)
procedure show;override;
procedure execute;override;
end;
thread2=class(tmythread)
procedure show;override;
procedure execute;override;
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure mythreadfunc;// 创建的线程函数
var
i:integer;
dc:hdc;
s:string;
begin
for i:=0 to 100000 do
begin
s:=inttostr(i);
dc:=getdc(form1.edit1.handle);
textout(dc,0,0,pchar(s),length(s));
releasedc(form1.edit1.handle,dc);
end;
end;
constructor tmythread.create(myedit1:tedit);// 创建线程
begin
inherited create(false);
myedit:=myedit1;
freeonterminate:=true; // 线程终止时自动删除对象,
end;
procedure thread1.show;// 类调用的线程函数
begin
myedit.Text:=inttostr(count);
end;
procedure thread1.execute;// 线程方法重载
var
i:integer;
begin
for i:=0 to 100000 do
begin
count:=i;
synchronize(show); // 线程调用同步
end;
end;
procedure thread2.show;// 类调用的线程函数
begin
mythreadfunc;
end;
procedure thread2.execute;// 线程方法重载
begin
synchronize(show); // 线程调用同步
end;
procedure TForm1.class1buttonClick(Sender: TObject);// 引用类方法创
建线程
begin
with thread1.create(edit1) do
end;
procedure TForm1.class2buttonClick(Sender: TObject);
// 引用类方法创
建线程
begin
with thread2.create(edit1) do
end;
procedure TForm1.routines1ButtonClick(Sender: TObject);
// 引用例程创建线程
var
hthread:thandle;
thid:dword;
begin
hthread:=beginthread(nil,0,@mythreadfunc,nil,0,thid);
if hthread=0 then
showmessage(' 创建线程失败');
end;
procedure TForm1.routines2ButtonClick(Sender: TObject);
// 用例程创建线程失败
begin
mythreadfunc;
end;
end.
点击“成功”按钮,该应用程序成功的创建了一个线程,在Edit1 框内不断计算数据的同时,改变窗体的大小或移动窗体均能实现。而在点击“失败”按钮后,会发现此应用程序中只能有一个主线程存在,在程序没有计算完之前根本不能做其它任何事情!此程序在基于Windows95 的Delphi3 和Delphi4 中均运行通过。
再谈在Delphi中利用线程
最新推荐文章于 2022-11-13 15:44:11 发布