delphi 線程教學第六節:TList與泛型


第六節: TList 與泛型
 
TList 是一個重要的容器,用途廣泛,配合泛型,更是如虎添翼。
我們先來改進一下帶泛型的 TList 基類,以便以后使用。
本例源碼下載(delphi XE8版本): FooList.Zip
 
unit  uFooList;
interface
uses
   Generics . Collections;
type
 
   TFooList <T>=  class (TList<T>)
   private
     procedure  FreeAllItems;
   protected
     procedure  FreeItem(Item: T);virtual;
     // 子類中需要重載此過程。以確定到底如何釋放 Item
     // 如果是 Item 是指針,就用 Dispose(Item);
     // 如果是 Item 是TObject ,就用 Item.free;
   public
     destructor  Destroy;override;
     procedure  ClearAllItems;
     procedure  Lock;   // 給本類設計一把鎖。
     procedure  Unlock;
   end ;
   // 定義加入到 List 的 Item 都由 List 來釋放。
   // 定義釋放規則很重要!只有規則清楚了,才不會亂套。
   // 通過這樣簡單的改造, TList 立馬好用 N 倍。
 
implementation
{ TFooList<T> }
 
procedure  TFooList<T>.ClearAllItems;
begin
   FreeAllItems;
   Clear;
end ;
 
destructor  TFooList<T>.Destroy;
begin
   FreeAllItems;
   inherited ;
end ;
 
procedure  TFooList<T>.FreeAllItems;
var
   Item: T;
begin
   for  Item  in  self  do
     FreeItem(Item);
end ;
 
procedure  TFooList<T>.FreeItem(Item: T);
begin
end ;
 
procedure  TFooList<T>.Lock;
begin
   System . TMonitor . Enter(self);
end ;
 
procedure  TFooList<T>.Unlock;
begin
   System . TMonitor . Exit(self);
end ;
 
end .
 
將第五節的例子用 TFooList 改寫:
 
unit  uFrmMain; 
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
     procedure  FreeItem(Item: TCountThread); override;  // 指定 Item 的釋放方式。
   end ;
 
   TNumList =  Class (TFooList< Integer >);  // 定義一個 Integer List
 
   TFrmMain =  class (TForm)
     memMsg: TMemo;
     edtNum: TEdit;
     btnWork: TButton;
     lblInfo: TLabel;
     procedure  FormCreate(Sender: TObject);
     procedure  FormDestroy(Sender: TObject);
     procedure  btnWorkClick(Sender: TObject);
     procedure  FormCloseQuery(Sender: TObject;  var  CanClose:  Boolean );
   private
     { Private declarations }
 
     FNumList: TNumList;
     FCountThreadList: TCountThreadList;
 
     FBuff: TStringList;
     FBuffIndex:  Integer ;
     FBuffMaxIndex:  Integer ;
     FWorkedCount:  Integer ;
 
     procedure  DispMsg(AMsg:  string );
     procedure  OnThreadMsg(AMsg:  string );
 
     function  OnGetNum(Sender: TCountThread):  Boolean ;
     procedure  OnCounted(Sender: TCountThread);
 
     procedure  LockCount;
     procedure  UnlockCount;
 
   public
     { Public declarations }
   end ;
 
var
   FrmMain: TFrmMain;
 
implementation
 
{ $R  *.dfm}
{ TFrmMain }
 
{ TCountThreadList }
procedure  TCountThreadList . FreeItem(Item: TCountThread);
begin
   inherited ;
   Item . Free;
end ;
 
procedure  TFrmMain . 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;
 
   for  thd  in  FCountThreadList  do
   begin
     thd . StartThread;
   end ;
 
end ;
 
procedure  TFrmMain . DispMsg(AMsg:  string );
begin
   memMsg . Lines . Add(AMsg);
end ;
 
procedure  TFrmMain . FormCloseQuery(Sender: TObject;  var  CanClose:  Boolean );
begin
   // 防止計算期間退出
   LockCount;  // 請思考,這里為什么要用 LockCount;
   CanClose := btnWork . Enabled;
   if  not  btnWork . Enabled  then
     DispMsg( '正在計算,不准退出!' );
   UnlockCount;
end ;
 
procedure  TFrmMain . FormCreate(Sender: TObject);
var
   thd: TCountThread;
   i:  Integer ;
begin
 
   FCountThreadList := TCountThreadList . Create;
   // 可以看出用了 List 之后,線程數量指定更加靈活。
   // 多個線程在一個 List 中,這個 List 可以理解為線程池。
   for  i :=  1  to  3  do
   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 ;
 
procedure  TFrmMain . FormDestroy(Sender: TObject);
begin
   FNumList . Free;
   FCountThreadList . Free;
end ;
 
procedure  TFrmMain . LockCount;
begin
   System . TMonitor . Enter(btnWork);
end ;
 
procedure  TFrmMain . UnlockCount;
begin
   System . TMonitor . Exit(btnWork);
end ;
 
procedure  TFrmMain . 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 );
 
   if  FWorkedCount >= FBuffMaxIndex +  1  then
   begin
     TThread . Synchronize( nil ,
       procedure
       begin
         DispMsg( '已計算完成' );
         btnWork . Enabled :=  true // 恢復按鈕狀態。
       end );
   end ;
 
   UnlockCount;
 
end ;
 
function  TFrmMain . OnGetNum(Sender: TCountThread):  Boolean ;
begin
   // 將多個線程訪問 FNumList 排隊。
   FNumList . Lock;
   try
     if  FBuffIndex > FBuffMaxIndex  then
     begin
       result :=  false ;
     end
     else
     begin
       Sender . Num := FNumList[FBuffIndex];
       result :=  true ;
       inc(FBuffIndex);
     end ;
   finally
     FNumList . Unlock;
   end ;
end ;
 
procedure  TFrmMain . OnThreadMsg(AMsg:  string );
begin
   TThread . Synchronize( nil ,
     procedure
     begin
       DispMsg(AMsg);
     end );
end ;
 
end .
 
通過這五節學習,相信大家已能掌握 delphi 線程的用法了。
下一節課程內容,我們將利用 TFooList 設計更高級實用的線程工具。也是本線程教程的完結篇。



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM