delphi 线程教学第六节:TList与泛型

第六节: TList 与泛型

TList 是一个重要的容器,用途广泛,配合泛型,更是如虎添翼。

我们先来改进一下带泛型的 TList 基类,以便以后使用。

本例源码下载(delphi XE8版本): FooList.Zip

unituFooList;

interface

uses

Generics.Collections;

type

TFooList <T>=class(TList<T>)

private

procedureFreeAllItems;

protected

procedureFreeItem(Item: T);virtual;

// 子类中需要重载此过程。以确定到底如何释放 Item

// 如果是 Item 是指针,就用 Dispose(Item);

// 如果是 Item 是TObject ,就用 Item.free;

public

destructorDestroy;override;

procedureClearAllItems;

procedureLock;// 给本类设计一把锁。

procedureUnlock;

end;

// 定义加入到 List 的 Item 都由 List 来释放。

// 定义释放规则很重要!只有规则清楚了,才不会乱套。

// 通过这样简单的改造, TList 立马好用 N 倍。

implementation

{ TFooList<T> }

procedureTFooList<T>.ClearAllItems;

begin

FreeAllItems;

Clear;

end;

destructorTFooList<T>.Destroy;

begin

FreeAllItems;

inherited;

end;

procedureTFooList<T>.FreeAllItems;

var

Item: T;

begin

forIteminselfdo

FreeItem(Item);

end;

procedureTFooList<T>.FreeItem(Item: T);

begin

end;

procedureTFooList<T>.Lock;

begin

System.TMonitor.Enter(self);

end;

procedureTFooList<T>.Unlock;

begin

System.TMonitor.Exit(self);

end;

end.

将第五节的例子用 TFooList 改写:

unituFrmMain;

interface

uses

Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,

Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, uCountThread, uFooList;

type

TCountThreadList =Class(TFooList<TCountThread>)// 定义一个线程 List

protected

procedureFreeItem(Item: TCountThread); override;// 指定 Item 的释放方式。

end;

TNumList =Class(TFooList<Integer>);// 定义一个 Integer List

TFrmMain =class(TForm)

memMsg: TMemo;

edtNum: TEdit;

btnWork: TButton;

lblInfo: TLabel;

procedureFormCreate(Sender: TObject);

procedureFormDestroy(Sender: TObject);

procedurebtnWorkClick(Sender: TObject);

procedureFormCloseQuery(Sender: TObject;varCanClose:Boolean);

private

{ Private declarations }

FNumList: TNumList;

FCountThreadList: TCountThreadList;

FBuff: TStringList;

FBuffIndex:Integer;

FBuffMaxIndex:Integer;

FWorkedCount:Integer;

procedureDispMsg(AMsg:string);

procedureOnThreadMsg(AMsg:string);

functionOnGetNum(Sender: TCountThread):Boolean;

procedureOnCounted(Sender: TCountThread);

procedureLockCount;

procedureUnlockCount;

public

{ Public declarations }

end;

var

FrmMain: TFrmMain;

implementation

{$R*.dfm}

{ TFrmMain }

{ TCountThreadList }

procedureTCountThreadList.FreeItem(Item: TCountThread);

begin

inherited;

Item.Free;

end;

procedureTFrmMain.btnWorkClick(Sender: TObject);

var

s:string;

thd: TCountThread;

begin

btnWork.Enabled :=false;

FWorkedCount :=0;

FBuffIndex :=0;

FBuffMaxIndex := FNumList.Count -1;

s :='共'+ IntToStr(FBuffMaxIndex +1) +'个任务,已完成:'+ IntToStr(FWorkedCount);

lblInfo.Caption := s;

forthdinFCountThreadListdo

begin

thd.StartThread;

end;

end;

procedureTFrmMain.DispMsg(AMsg:string);

begin

memMsg.Lines.Add(AMsg);

end;

procedureTFrmMain.FormCloseQuery(Sender: TObject;varCanClose:Boolean);

begin

// 防止计算期间退出

LockCount;// 请思考,这里为什么要用 LockCount;

CanClose := btnWork.Enabled;

ifnotbtnWork.Enabledthen

DispMsg('正在计算,不准退出!');

UnlockCount;

end;

procedureTFrmMain.FormCreate(Sender: TObject);

var

thd: TCountThread;

i:Integer;

begin

FCountThreadList := TCountThreadList.Create;

// 可以看出用了 List 之后,线程数量指定更加灵活。

// 多个线程在一个 List 中,这个 List 可以理解为线程池。

fori :=1to3do

begin

thd := TCountThread.Create(false);

FCountThreadList.Add(thd);

thd.OnStatusMsg := self.OnThreadMsg;

thd.OnGetNum := self.OnGetNum;

thd.OnCounted := self.OnCounted;

thd.ThreadName :='线程'+ IntToStr(i);

end;

FNumList := TNumList.Create;

// 构造一组数据用来测试

FNumList.Add(100);

FNumList.Add(136);

FNumList.Add(306);

FNumList.Add(156);

FNumList.Add(152);

FNumList.Add(106);

FNumList.Add(306);

FNumList.Add(156);

FNumList.Add(655);

FNumList.Add(53);

FNumList.Add(99);

FNumList.Add(157);

end;

procedureTFrmMain.FormDestroy(Sender: TObject);

begin

FNumList.Free;

FCountThreadList.Free;

end;

procedureTFrmMain.LockCount;

begin

System.TMonitor.Enter(btnWork);

end;

procedureTFrmMain.UnlockCount;

begin

System.TMonitor.Exit(btnWork);

end;

procedureTFrmMain.OnCounted(Sender: TCountThread);

var

s:string;

begin

LockCount;

// 锁不同的对象,宜用不同的锁。

// 每把锁的功能要单一,锁的粒度要最小化。才能提高效率。

s := Sender.ThreadName +':'+ IntToStr(Sender.Num) +'累加和为:';

s := s + IntToStr(Sender.Total);

OnThreadMsg(s);

inc(FWorkedCount);

s :='共'+ IntToStr(FBuffMaxIndex +1) +'个任务,已完成:'+ IntToStr(FWorkedCount);

TThread.Synchronize(nil,

procedure

begin

lblInfo.Caption := s;

end);

ifFWorkedCount >= FBuffMaxIndex +1then

begin

TThread.Synchronize(nil,

procedure

begin

DispMsg('已计算完成');

btnWork.Enabled :=true;// 恢复按钮状态。

end);

end;

UnlockCount;

end;

functionTFrmMain.OnGetNum(Sender: TCountThread):Boolean;

begin

// 将多个线程访问 FNumList 排队。

FNumList.Lock;

try

ifFBuffIndex > FBuffMaxIndexthen

begin

result :=false;

end

else

begin

Sender.Num := FNumList[FBuffIndex];

result :=true;

inc(FBuffIndex);

end;

finally

FNumList.Unlock;

end;

end;

procedureTFrmMain.OnThreadMsg(AMsg:string);

begin

TThread.Synchronize(nil,

procedure

begin

DispMsg(AMsg);

end);

end;

end.

通过这五节学习,相信大家已能掌握 delphi 线程的用法了。

下一节课程内容,我们将利用 TFooList 设计更高级实用的线程工具。也是本线程教程的完结篇。