再谈在Delphi中利用线程

南京陆军指挥学院研究生队 李晓峰

  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中利用线程)