第六节: 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 设计更高级实用的线程工具。也是本线程教程的完结篇。


delphi 线程教学第六节:TList与泛型的更多相关文章

  1. delphi 线程教学第五节:多个线程同时执行相同的任务

    第五节:多个线程同时执行相同的任务   1.锁   设,有一个房间 X ,X为全局变量,它有两个函数  X.Lock 与 X.UnLock; 有如下代码:   X.Lock;      访问资源 P; ...

  2. delphi 线程教学第四节:多线程类的改进

    第四节:多线程类的改进   1.需要改进的地方   a) 让线程类结束时不自动释放,以便符合 delphi 的用法.即 FreeOnTerminate:=false; b) 改造 Create 的参数 ...

  3. delphi 线程教学第七节:在多个线程时空中,把各自的代码塞到一个指定的线程时空运行

    第七节:在多个线程时空中,把各自的代码塞到一个指定的线程时空运行     以 Ado 为例,常见的方法是拖一个 AdoConnection 在窗口上(或 DataModule 中), 再配合 AdoQ ...

  4. delphi 线程教学第二节:在线程时空中操作界面(UI)

    第二节:在线程时空中操作界面(UI)   1.为什么要用 TThread ?   TThread 基于操作系统的线程函数封装,隐藏了诸多繁琐的细节. 适合于大部分情况多线程任务的实现.这个理由足够了吧 ...

  5. delphi 线程教学第一节:初识多线程

    第一节:初识多线程   1.为什么要学习多线程编程?   多线程(多个线程同时运行)编程,亦可称之为异步编程. 有了多线程,主界面才不会因为耗时代码而造成“假死“状态. 有了多线程,才能使多个任务同时 ...

  6. delphi 线程教学第一节:初识多线程(讲的比较浅显),还有三个例子

    http://www.cnblogs.com/lackey/p/6297115.html 几个例子: http://www.cnblogs.com/lackey/p/5371544.html

  7. delphi 线程教学第三节:设计一个有生命力的工作线程

    第三节:设计一个有生命力的工作线程   创建一个线程,用完即扔.相信很多初学者都曾这样使用过. 频繁创建释放线程,会浪费大量资源的,不科学.   1.如何让多线程能多次被复用?   关键是不让代码退出 ...

  8. ASP.NET MVC深入浅出系列(持续更新) ORM系列之Entity FrameWork详解(持续更新) 第十六节:语法总结(3)(C#6.0和C#7.0新语法) 第三节:深度剖析各类数据结构(Array、List、Queue、Stack)及线程安全问题和yeild关键字 各种通讯连接方式 设计模式篇 第十二节: 总结Quartz.Net几种部署模式(IIS、Exe、服务部署【借

    ASP.NET MVC深入浅出系列(持续更新)   一. ASP.NET体系 从事.Net开发以来,最先接触的Web开发框架是Asp.Net WebForm,该框架高度封装,为了隐藏Http的无状态模 ...

  9. TMsgThread, TCommThread -- 在delphi线程中实现消息循环

    http://delphi.cjcsoft.net//viewthread.php?tid=635 在delphi线程中实现消息循环 在delphi线程中实现消息循环 Delphi的TThread类使 ...

随机推荐

  1. python CSS

    CSS 一. css的四种引入方式   1.行内式  2.嵌入式  3. 链接式 将一个.css文件引入到HTML文件中 1 <link href="mystyle.css" ...

  2. TP-LINK | TL-WR842N设置无线转有线

    首先点击右上角的"高级设置". 点击左侧的"无线设置"栏,点击"WDS无线桥接",然后一步步设置可以使路由器连接到当前的一个无线网络. 然后 ...

  3. 扩展第二屏幕发生Out Of Range及扩展后耳机没声音解决方案

    新年好\(^o^)/~ 拓展屏幕这种事情.其实很简单的.无非就分辨率跟刷新频率.有时候一接好就可以了.有时候怎么也弄不出来.我也是搞了蛮久.昨天突然弄通了.小记一下说不定能帮到同需求人. [设备] 主 ...

  4. Gold well平台罗琪:叙利亚战火令黄金看涨意愿强烈

    Gold well平台罗琪:叙利亚战火令黄金看涨意愿强烈基本面分析:纸黄金交易通网显示,全球最大黄金上市交易基金(ETF)截至04月14日黄金持仓量较上日持平,当前持仓量为865.89吨,本月止净增持 ...

  5. [LeetCode] My Calendar I 我的日历之一

    Implement a MyCalendar class to store your events. A new event can be added if adding the event will ...

  6. 使用Nwjs开发桌面应用体验

    之前一直用.net开发桌面应用,最近由于公司需要转为nodejs,但也是一直用nodejs开发后台应用,网站,接口等.近期,需要开发一个客户端,想着既然nodejs号称全栈,就试一下开发桌面应用到底行 ...

  7. ●UVa 11346 Probability

    题链: https://vjudge.net/problem/UVA-11346题解: 连续概率,积分 由于对称性,我们只用考虑第一象限即可. 如果要使得面积大于S,即xy>S, 那么可以选取的 ...

  8. ●BZOJ 3129 [Sdoi2013]方程

    题链: http://www.lydsy.com/JudgeOnline/problem.php?id=3129 题解: 容斥,扩展Lucas,中国剩余定理 先看看不管限制,只需要每个位置都是正整数时 ...

  9. java版的类似飞秋的局域网在线聊天项目

    原文链接:http://www.cnblogs.com/wangleiblog/articles/5323305.html 转载请注明 最近在弄一个java版的局域网在线聊天项目,功能跟飞秋差不多.p ...

  10. 用ECMAScript4 ( ActionScript3) 实现Unity的热更新 -- 操作符重载和隐式类型转换

    C#中,某些类型会定义隐式类型转换和操作符重载.Unity中,有些对象也定义了隐式类型转换和操作符重载.典型情况有:UnityEngine.Object.UnityEngine.Object的销毁是调 ...