delphi 线程教学第四节:多线程类的改进
unit
uFooThread;
interface
uses
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
;
// 未说明之处,请参考面向对象设计基础知识。
implementation
uses
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;
begin
end
;
end
.
unit
uCountThread;
interface
uses
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;
interface
uses
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 ...
随机推荐
- Python/零起点(一、数字及元组)
Python/零起点(一.数字及元组) int整型 int()强行转换成整型数据类型 int整型是不可变,且是不可迭代的对象 一.整型数字用二进制位数表示案例: age=7 #设定一个数字赋值给age ...
- python——常用模块
python--常用模块 1 什么是模块: 模块就是py文件 2 import time #导入时间模块 在Python中,通常有这三种方式来表示时间:时间戳.元组(struct_time).格式化的 ...
- Spark:spark df插入hive表后小文件数量多,如何合并?
在做spark开发过程中,时不时的就有可能遇到租户的hive库目录下的文件个数超出了最大限制问题. 一般情况下通过hive的参数设置: val conf = new SparkConf().setAp ...
- 字典的update方法
>>> dict = {"name":"zara", "age": 7} >>> dict2 = {&q ...
- 多线程编程、java图形用户界面编程、Java I / O系统
线程概述 进程:是一种 “自包容”的运行程序 线程是进程当中的一个概念,最小处理单位 THread类.Runnable接口.Object类 创建新执行线程有两种方法:1:一种方法是将类声明为Threa ...
- PHP 页面跳转到另一个页面的多种方法方法总结
如何在PHP中从一个页面重定向到另外一个页面呢?这里列出了三种办法,供参考. 一.用HTTP头信息 也就是用PHP的HEADER函数.PHP里的HEADER函数的作用就是向浏览器发出由HTTP协议规定 ...
- BST讲解
BST 第一步,什么是BST,所谓BST就是满足一种特定性质的二叉树,这个性质一般情况是当前节点的权值比他的左子树的所有点的权值大,比他的右子树的所有点的权值小,满足这样性质的二叉树就称为BST,下面 ...
- js高阶函数应用—函数柯里化和反柯里化(二)
第上一篇文章中我们介绍了函数柯里化,顺带提到了偏函数,接下来我们继续话题,进入今天的主题-函数的反柯里化. 在上一篇文章中柯里化函数你可能需要去敲许多代码,理解很多代码逻辑,不过这一节我们讨论的反科里 ...
- 51nod 1270 数组的最大代价
1270 数组的最大代价题目来源: HackerRank基准时间限制:1 秒 空间限制:131072 KB 分值: 20 难度:3级算法题 数组A包含N个元素A1, A2......AN.数组B包含N ...
- UESTC 618 无平方因子数 ( 莫比乌斯)
UESTC 618 题意:求1到n中无平方因子数的个数 Sample Input 3 1 10 30 Sample Output 1 7 19 思路:与前面的BZOJ 2440相似 #inc ...