您的位置:首页 > 编程语言 > Delphi

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

2017-01-18 16:15 489 查看
第二节:在线程时空中操作界面(UI)

1.为什么要用 TThread ?

TThread 基于操作系统的线程函数封装,隐藏了诸多繁琐的细节。
适合于大部分情况多线程任务的实现。这个理由足够了吧?
什么?你要用 windows 的线程 api 来实现多线程?
我可以负责任地告诉你,如果你用 api 来实现多线程任务,
加之你天资聪明,对 delphi 的面向对象思想掌握得非常快,
那么最终也你也会写一个与 TThread 类似的东西来提高开发效率。
何必折腾呢?要相信 delphi 的工程师,人家早已看透了一切。咳咳。
同理,要相信微软的工程师,windows 操作系统是没有啥大问题的。
更同理,要相信设计手机的工程师,不需要贴膜,人家好不容易把才手机变薄的。
哈哈,扯远了。。。
(本教程默认操作系统为 windows 7/10 , delphi 的版本为 XE8,大多数代码均能在 XE2 上运行)

2.线程时空中操作界面(UI)到底有什么门道?

很多教程中都一再强调,线程时空里,不准直接去更新 UI ,但似乎没有说明原因。
我们假设UI 界面允许多个线程同时去更新,看看会发生什么情况。
如果两个线程,同时都在界面相同的区域进行画图操作,比如一个要画绿色,一个要红色,
那么最终,界面上是不是可能出现一个大花脸?
可以这样朴实地理解,就知道为什么 UI 不允许多线程去操作了。不是不能,是不得已。
(线程中不允许直接操作 UI,在安卓下同样适用)

3. TThread.Synchronize() 原理。

是用 SendMessage 函数,发了一个 WM_NULL 消息给窗口。
窗口接到消息后再去更新界面。窗口消息响应事件可以理解为主线程时空。

以下是接上节的实例,来看如何正确地显示计算结果在窗口上。

unit
Unit10;

interface

uses

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

Vcl
.
Controls, Vcl
.
Forms, Vcl
.
Dialogs, uAccumulation, Vcl
.
StdCtrls;

type

TForm10 =
class
(TForm)

Edit1: TEdit;

Button1: TButton;

procedure
Button1Click(Sender: TObject);

private

procedure
OnAccumulated(Sender: TAccumulationThread);

end
;


implementation

{
$R
*.dfm}


procedure
TForm10
.
Button1Click(Sender: TObject);

var

accThread: TAccumulationThread;

begin

accThread := TAccumulationThread
.
Create(
true
);

accThread
.
OnAccumulated := self
.
OnAccumulated;
//指定事件。

accThread
.
FreeOnTerminate :=
true
;
// 线程结束后自动释放

accThread
.
Num:=
100
;

accThread
.
Start;

end
;


procedure
TForm10
.
OnAccumulated(Sender: TAccumulationThread);

begin

// 这里是线程时空

// 要更新 UI ,要用 Synchorinize 把更新的操作

// 塞到主线程时空里去运行。注意理解:“塞!”

TThread
.
Synchronize(
nil
,

procedure

begin

// 这里的代码被塞到主线程时空里去了。

Edit1
.
Text := inttostr(Sender
.
Total);

end
);

// Synchronize 第一个参数是 nil

// 第二个参数是一个匿名函数 什么是匿名函数? 以后会介绍到。

end
;

end
.


unit
uAccumulation;

interface

uses

Classes;

type

TAccumulationThread =
class
;
//此为提前申明

TOnAccumulated =
procedure
(Sender: TAccumulationThread)
of
object
;

// 如果不提前申明,Sender 就要定义成 TObject

// 在事件函数中,要操作 Sender 就需要强制转换

TAccumulationThread =
class
(TThread)

protected

procedure
Execute;override;

public

Num:
integer
;

Total:
integer
;

OnAccumulated: TOnAccumulated;

end
;


implementation


procedure
TAccumulationThread
.
Execute;

var

i:
integer
;

begin

Total :=
0
;

if
Num>
0
then

begin

for
i :=
1
to
Num
do

Total :=Total + i

end
;

// 当计算完成后,就调用OnAccumulated 通知调用者

if
Assigned(OnAccumulated)
then

OnAccumulated(self);

end
;

end
.


4. 哪些代码运行在线程时空?

Execute 函数中运行的、调用的代码,都是”线程代码“。与代码书写位置无关!!!
Sysnchronize 是个特殊的存在,它可以在线程时空里,把代码塞到主线程时空里去运行。

第三节,将实现线程如何保持生命力,创建后可以反复使用。慢慢进入实用阶段了,请不要错过。

delphi 线程教学第三节:设计一个有生命力的工作线程
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: