多线程开篇
这是一篇很枯燥的文章,可能很多人会觉得没有什么卵用。但是只有在搞清楚理论之后才可以完美的去实现。所以这篇文章作为多线程的开篇
其实多线程是一个很大的话题,我也仅仅停留在简单的应用,至于以后的深入只能靠自己
时间切片
在大多数支持多线程的系统中,可能有许多用户在计算机系统上同时发出请求。通常,系统中的物理处理器数量少于可能并行运行的线程数量。而此时就需要依靠时间切片完成,大多数系统都支持时间切片,也称为先发制人的多任务处理。
在时间切片的系统中,线程会运行一段时间,然后被抢占; 也就是说,硬件计时器触发,导致操作系统重新评估应运行哪些线程,可能停止对当前运行的线程执行,以及运行最近未执行的其他线程。这甚至允许单处理器机器运行多个线程。在PC上,时间片往往大约是五十五毫秒。
55 毫秒的时间给人的感觉接近无感,以为是多个任务在同时运行。线程不应该改变程序的语义。他们只是改变了操作的时间。
下面几种场景我们都需要多线程的方式来完成任务
进行冗长的处理:当Windows应用程序正在计算时,它无法再处理任何消息。结果,无法更新显示。
进行后台处理:某些任务可能不是时间关键,但需要连续执行。
执行I / O工作:磁盘或网络的I / O可能会出现不可预测的延迟。线程允许您确保I / O延迟不会延迟应用程序的不相关部分。
时间切片的概念也不小,这里不做深入讨论,仅仅产生一个认知即可
Delphi支持两种线程的启动方式,当然也有可能是 3 种
Win32API
TTHread类
线程池:这种方式我没有用到,同时在掌握上面两种方式之后线程池自然也会掌握,所以我们把学习的重点放在前面两种
Delphi使得线程启动变得容易。在获取子线程执行之前,通常需要在线程中设置一些初始状态。通过创建挂起的线程(构造函数的参数),可以确保线程中的所有代码都不会执行,直到线程恢复为止。
# API启动
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
btn2: TButton;
procedure btn2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
{工作线程调入函数,stdcall用于多个线程排序以及系统级别调用加此关键字}
function MyFun(p:Pointer):integer;stdcall;
var
i:integer;
begin
for i := 0 to 500000 do
begin
with Form1.Canvas do
begin
Lock;
{50和10是坐标X和Y}
TextOut(50,10,IntToStr(i));
Unlock;
Application.ProcessMessages;
end;
end;
end;
{工作线程,拖动窗口时计数不会停顿,因为和主线程分开工作了}
procedure TForm1.btn2Click(Sender: TObject);
var
{用于接收线程返回句柄,也可以用DWORD}
ID:THandle;
begin
{API创建线程}
CreateThread(nil,0,@MyFun,nil,0,ID);
end;
end.
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# TThread
其实在Delphi中更建议使用该类完成线程相关的操作
unit UnitThread;
interface
uses
Vcl.Forms, Vcl.Dialogs, System.SysUtils, System.Classes;
type
TMyThread = class(TThread)
protected
procedure Execute; override;
end;
implementation
uses
UnitMain;
{ TMyThread }
procedure TMyThread.Execute;
var
I: Integer;
begin
FreeOnTerminate := False;
I := 1;
while True do begin
if FreeOnTerminate then
Exit;
form1.lbl1.Caption := '线程ID:' + Self.ThreadID.ToString + ':' + I.ToString;
TThread.Sleep(300);
I := I + 1;
end;
end;
end.
{启动线程的代码}
procedure TForm1.btn1Click(Sender: TObject);
begin
Thread := TMyThread.Create(True);
Thread.Start;
end;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
通过代码可以很明显的发现 FreeOnTerminate 这玩意儿是可以控制线程的启停
# OnTerminate事件
当线程真正完成执行时,会发生OnTerminate事件。它并没有在终止线程的方法被调用发生。此事件可能非常有用,因为它在主VCL线程的上下文中执行,就像传递给同步的方法一样。因此,如果一个人希望用一个在终止时自动释放自己的线程来执行一些VCL操作,那么这就是它的地方。
# 注意事项
要记住Execute()需要经常地检查Terminated属性的值,来确认是否要提前退出。尽管这将意味着当使用线程工作的时候,你必须关心更多的事情,但它能确保在线程结束时,能够完成必要的清除
在某些紧急情况下,你可以使用Win32 API函数 TerminateThread()来终止一个线程。但是,除非没有别的办法了,否则不要使用它。
在Delphi中还可以使用TThread类的CreateAnonymousThread函数来创建线程