delphi 线程教学第四节:多线程类的改进
unit uFooThread;interfaceuses System.Classes, System.SyncObjs;type TOnMsg = procedure(AMsg: string) of object; // 定义一个用于输出信息的事件 // 很多编程资料推荐在 String 参数前面加 const ,以提高效率 // 我的理由是为了代码美观。如果有多个参数,加上 const 参数太长了。 // 在以后的使用中,请自己斟酌是否加 const 。 TFooThread = class(TThread) private FEvent: TEvent; FCanAccessCom: Boolean; FRunningInThread: TThreadMethod; // TThreadMethod 的定义是 TThreadMethod = Procedure of object; // 意为这个 Procedure 是写在一个类中的。 // 在其它编程语言中,TThreadMethod 被称为函数指针。 // FRunningInThread 它用来保存将要在线程中运行的代码或 Procedure procedure DoExecute; protected // protected 段中定义的变量与函数,允许在子类中调用。 procedure Execute; override; procedure DoOnStatusMsg(AMsg: string); procedure ExecProcInThread(AProc: TThreadMethod); public constructor Create(ACanAccessCOM: Boolean); reintroduce; // reintroduce 是再引入 Create 的参数的意思。 destructor Destroy; override; procedure StartThread; virtual; public OnStatusMsg: TOnMsg; // 亦可改写为 Property OnStatusMsg:TOnMsg Read FOnMsg write SetOnMsg; // 太啰嗦了,如果不再对 SetOnMsg 进行操作,建议这样写。 // 如果后期需要改动,原来的代码亦可以不变。 end; // 未说明之处,请参考面向对象设计基础知识。implementationuses ActiveX, SysUtils;constructor TFooThread.Create(ACanAccessCOM: Boolean);begin inherited Create(false); FEvent := TEvent.Create(nil, true, false, ''); FreeOnTerminate := false;end;destructor TFooThread.Destroy;begin // 此处我们要设计手动 Free 的调用。 Terminate; // 首先要将 Terminated 设置为 true; FEvent.SetEvent; // 启动线程。 WaitFor; // 此 waitfor 的意思是等待线程退出 Execute // 此 WaitFor 是 TThread 类的。注意与 FEvent.WaitFor 区别 // 本质上,它们都是操作系统提供的信号的等待功能。 // 有兴趣可以直接参考系统源码 ( delphi 提供的源码 ) FEvent.Free; inherited;end;procedure TFooThread.DoExecute;begin FEvent.WaitFor; FEvent.ResetEvent; while not Terminated do begin try FRunningInThread; // 因为它是一个 Procedure ,故可直接运行。 except // 捕捉异常,否则异常发生时代码将退出 Execute ,线程生命周期就结束了。 on e: Exception do begin DoOnStatusMsg('ThreadErr:' + e.Message); end; end; FEvent.WaitFor; FEvent.ResetEvent; end;end;procedure TFooThread.DoOnStatusMsg(AMsg: string);begin // 这是引发事件常用的写法。 if Assigned(OnStatusMsg) then OnStatusMsg(AMsg);end;procedure TFooThread.ExecProcInThread(AProc: TThreadMethod);begin FRunningInThread := AProc; FEvent.SetEvent; // 启动线程。 // 需要说明的是,第一次运行本函数 ExecProcInThread 一般是在主线程时空里运行。 // 第二次运行本函数可以设计为在线程时空中运行,后面章节会讲到。 // 其作用是把 AProc 塞到线程时空中并启动线程。end;procedure TFooThread.Execute;begin if FCanAccessCom then begin CoInitialize(nil); // 在线程中初始化 COM ,反正调用了此句,才能在线程中使用 COM // 这是 windows 操作系统规定的,与 delphi 没有关系。 // 你用 api 操作线程,在线程中访问 COM 同样需要这样做。 try DoExecute; finally CoUninitialize; // 与初始化对应,解除线程访问 COM 的能力。 end; end else DoExecute;end;procedure TFooThread.StartThread;beginend;end.unit uCountThread;interfaceuses uFooThread;type TCountThread = class; TOnCounted = procedure(Sender: TCountThread) of object; TCountThread = class(TFooThread) private procedure Count; procedure DoOnCounted; public procedure StartThread; override; public Num: integer; Total: integer; OnCounted: TOnCounted; end;implementation{ TCountThread }procedure TCountThread.Count;var i: integer;begin DoOnStatusMsg('开始计算...'); Total := 0; if Num > 0 then for i := 1 to Num do begin Total := Total + i; sleep(10); // 故意变慢,实际代码请删除此行。 // 实际上为确保线程能够及时退出 // 此处还应加上一个判断是否出的标志,请大家自行思考。 // 这又是一个两难的选择。 // 加了判断标志,退出容易了,但效率又低了。 // 所以,编程人员总是在效率与友好性中做出选择。 // 且编且珍惜。 end; DoOnCounted; //引发 OnCounted 事件,告知调用者。 DoOnStatusMsg('计算完成...');end;procedure TCountThread.DoOnCounted;begin // if Assigned(OnCounted) then // 等价于 if OnCounted <> nil then if Assigned(OnCounted) then OnCounted(self);end;procedure TCountThread.StartThread;begin inherited; ExecProcInThread(Count); // 把 Count 过程塞到线程中运行。end;end.unit uFrmMain;interfaceuses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, uCountThread;type TFrmMain = class(TForm) memMsg: TMemo; edtNum: TEdit; btnWork: TButton; procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure btnWorkClick(Sender: TObject); private { Private declarations } FCountThread: TCountThread; // 取名是一直是个有技术含量的事情。 // 推荐去掉类名的 T 换成 F 这样的写法。 procedure DispMsg(AMsg: string); procedure OnThreadMsg(AMsg: string); procedure OnCounted(Sender: TCountThread); public { Public declarations } end;var FrmMain: TFrmMain;implementation{$R *.dfm}{ TFrmMain }procedure TFrmMain.btnWorkClick(Sender: TObject);var n: integer;begin btnWork.Enabled := false; n := StrToIntDef(edtNum.Text, 0); FCountThread.Num := n; FCountThread.StartThread;end;procedure TFrmMain.DispMsg(AMsg: string);begin memMsg.Lines.Add(AMsg);end;procedure TFrmMain.FormCreate(Sender: TObject);begin FCountThread := TCountThread.Create(false); // 此处不需要访问 Com 所以用 false FCountThread.OnStatusMsg := self.OnThreadMsg; // 因为是在线程时空中引发的消息,故这里用了 OnThreadMsg; FCountThread.OnCounted := self.OnCounted;end;procedure TFrmMain.FormDestroy(Sender: TObject);begin // 这里要注意,尽管我们在 TFooThread 中的析构函数中 // 写了保证线程退出的函数。那也只是以防万一的。 // 在线程手动 Free 之前,一定要确保线程代码已经退出了 Execute // 为了友好退出,又需要在计算代码中加入判断是否退出的标志。 // 请参考 TCountThread Count 中的注释。 // 本教程一直反复强调“代码退出Execute”这个概念。 // 用线程,就得负责一切,不可偷懒! FCountThread.Free;end;procedure TFrmMain.OnCounted(Sender: TCountThread);var s: string;begin s := IntToStr(Sender.Num) + '累加和为:'; s := s + IntToStr(Sender.Total); OnThreadMsg(s); // 因为这里是线程空间,所以需要用本函数。 // 而不是 DispMsg; // 网络组件,它的数据到达事件,其实是线程时空。要显示信息 // 也需要 Synchronize; 这是很多初学者易犯的错误。 // 如果在线程时空中,不用 Synchronize 来操作 UI,就会出现时灵时不灵的状态。 // 初学者所谓的运行不稳定,调试时又是正常。往往原因就是如此。 TThread.Synchronize(nil, procedure begin btnWork.Enabled := true; // 恢复按钮状态。 end);end;procedure TFrmMain.OnThreadMsg(AMsg: string);begin TThread.Synchronize(nil, procedure begin DispMsg(AMsg); end);end;end.delphi 线程教学第四节:多线程类的改进的更多相关文章
- delphi 线程教学第六节:TList与泛型
第六节: TList 与泛型 TList 是一个重要的容器,用途广泛,配合泛型,更是如虎添翼. 我们先来改进一下带泛型的 TList 基类,以便以后使用. 本例源码下载(delphi XE8版本) ...
- delphi 线程教学第五节:多个线程同时执行相同的任务
第五节:多个线程同时执行相同的任务 1.锁 设,有一个房间 X ,X为全局变量,它有两个函数 X.Lock 与 X.UnLock; 有如下代码: X.Lock; 访问资源 P; ...
- delphi 线程教学第七节:在多个线程时空中,把各自的代码塞到一个指定的线程时空运行
第七节:在多个线程时空中,把各自的代码塞到一个指定的线程时空运行 以 Ado 为例,常见的方法是拖一个 AdoConnection 在窗口上(或 DataModule 中), 再配合 AdoQ ...
- delphi 线程教学第一节:初识多线程
第一节:初识多线程 1.为什么要学习多线程编程? 多线程(多个线程同时运行)编程,亦可称之为异步编程. 有了多线程,主界面才不会因为耗时代码而造成“假死“状态. 有了多线程,才能使多个任务同时 ...
- delphi 线程教学第二节:在线程时空中操作界面(UI)
第二节:在线程时空中操作界面(UI) 1.为什么要用 TThread ? TThread 基于操作系统的线程函数封装,隐藏了诸多繁琐的细节. 适合于大部分情况多线程任务的实现.这个理由足够了吧 ...
- delphi 线程教学第一节:初识多线程(讲的比较浅显),还有三个例子
http://www.cnblogs.com/lackey/p/6297115.html 几个例子: http://www.cnblogs.com/lackey/p/5371544.html
- delphi 线程教学第三节:设计一个有生命力的工作线程
第三节:设计一个有生命力的工作线程 创建一个线程,用完即扔.相信很多初学者都曾这样使用过. 频繁创建释放线程,会浪费大量资源的,不科学. 1.如何让多线程能多次被复用? 关键是不让代码退出 ...
- 多线程的基本概念和Delphi线程对象Tthread介绍
多线程的基本概念和Delphi线程对象Tthread介绍 作者:xiaoru WIN 98/NT/2000/XP是个多任务操作系统,也就是:一个进程可以划分为多个线程,每个线程轮流占用CPU运行 ...
- Unity多线程(Thread)和主线程(MainThread)交互使用类——Loom工具分享
Unity多线程(Thread)和主线程(MainThread)交互使用类——Loom工具分享 By D.S.Qiu 尊重他人的劳动,支持原创,转载请注明出处:http.dsqiu.iteye.com ...
随机推荐
- [转]安卓新一代多渠道打包工具Walle 解决渠道包V2签名问题
转自https://www.jianshu.com/p/572b59829a08 为什么要打多个渠道的包? 大家都知道,android应用商店大大小小有几百个,作为一个有志向的app,就需要做到统计各 ...
- CodeForces 1B-字符串,进制转换与数学
一个萌新的成长之路 Background 同学们都回家了,只有我和wjh还有邢神在机房敲代码,吃random口味的方便面-- Description Translated by @PC_DOS fro ...
- flask开发表单
from flask import Flask from flask import render_template from flask import request from flask impor ...
- html<!DOCTYPE>声明标签
html<!DOCTYPE>声明标签 <DOCTYPE>声明是html文档的第一行,位于<html>标签之前 <DOCTYPE>声明不是html标签,他 ...
- JS面向对象使用面向对象进行开发
面向对象基础一之初体验使用面向对象进行开发 对 JS 中的面向对象的基础进行讲述, 初体验使用面向对象进行开发 主要内容是 面向对象的概念及特性 用面向对象的方式解决简单的标签创建实例 一些基础的 ...
- JEECG中出现Java.sql.SQLException: Value 'xxxx' can not be represented as java.sql.Timestamp的解决办法
出现`Java.sql.SQLException: Value 'xxxx' can not be represented as java.sql.Timestamp',其中xxxx部分对应包含一个看 ...
- 初入HTML5
在最开始接触HTML5的时候,你会遇到的大多是一些常见常用的属性以及属性值.它们分类广.品种杂且使用率高.到css各种样式的时候,你会接触到更多的东西,各种属性.选择器.盒子模型都是重点.那么,现在我 ...
- jQuery系列 第二章 jQuery框架使用准备
第二章 jQuery框架使用准备 2.1 jQuery框架和JavaScript加载模式对比 jQuery框架的加载模式 <script> window.onload = function ...
- ASwipeLayout一个强大的侧滑菜单控件
Android中侧滑的场景有很大,大部分是基于RecyclerView,但是有些时候你可以动态地addView到一个布局当中,也希望它实现侧滑,所以就产生了ASwipeLayout,该控件不仅支持在R ...
- Docker And Swarm Mode(一)
(一)节点的创建和配置 前言 虽然工作中一直在用Docker和Docker Swarm,但是总感觉有点陌生,总想自己亲手来写写和配置Docker 容器相关的事情,这篇文章主要是参考了Los Tech ...