delphi线程分析

合集下载

Delphi关于多线程同步的一些方法(转)

Delphi关于多线程同步的一些方法(转)

Delphi关于多线程同步的⼀些⽅法(转)线程是进程内⼀个相对独⽴的、可调度的执⾏单元。

⼀个应⽤可以有⼀个主线程,⼀个主线程可以有多个⼦线程,⼦线程还可以有⾃⼰的⼦线程,这样就构成了多线程应⽤了。

由于多个线程往往会同时访问同⼀块内存区域,频繁的访问这块区域,将会增加产⽣线程冲突的概率。

⼀旦产⽣了冲突,将会造成不可预料的结果(该公⽤区域的值是不可预料的)可见处理线程同步的必要性。

注意:本⽂中出现的所有代码都是⽤DELPHI描述的,调试环境为Windows me ,Delphi 6。

其中所涉及的Windows API函数可以从MSDN获得详细的。

⾸先引⽤⼀个实例来引出我们以下的讨论,该实例没有采取任何措施来避免线程冲突,它的主要过程为:由主线程启动两个线程对letters这个全局变量进⾏频繁的读写,然后分别把修改的结果显⽰到ListBox中。

由于没有同步这两个线程,使得线程在修改letters时产⽣了不可预料的结果。

ListBox中的每⼀⾏的字母都应该⼀致,但是上图画线处则不同,这就是线程冲突产⽣的结果。

当两个线程同时访问该共享内存时,⼀个线程还未对该内存修改完,另⼀个线程⼜对该内存进⾏了修改,由于写值的过程没有被串⾏化,这样就产⽣了⽆效的结果。

可见线程同步的重要性。

以下是本例的代码 unit.pas⽂件 unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; //定义窗⼝类 type TForm1 = class(TForm) ListBox1: TListBox; ListBox2: TListBox; Button1: TButton; procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations } end; //定义线程类 type TListThread=class(TThread) private Str:String; protected procedure AddToList;//将Str加⼊ListBox Procedure Execute;override; public LBox:TListBox; end; //定义变量 var Form1: TForm1; Letters:String='AAAAAAAAAAAAAAAAAAAA';//全局变量 implementation {$R *.dfm} //线程类实现部分 procedure TListThread.Execute; var I,J,K:Integer; begin for i:=0 to 50 do begin for J:=1 to 20 do for K:=1 to 1000 do//循环1000次增加产⽣冲突的⼏率 if letters[j]<'Z' then letters[j]:=succ(Letters[j]) else letters[j]:='A'; str:=letters; synchronize(addtolist);//同步访问VCL可视 end; end; procedure TListThread.AddToList; begin LBox.Items.Add(str);//将str加⼊列表框 end; //窗⼝类实现部分 procedure TForm1.Button1Click(Sender: TObject); var th1,th2:TListThread; begin Listbox1.Clear; Listbox2.Clear; th1:=tlistThread.Create(true);//创建线程1 th2:=tlistThread.Create(true);//创建线程2 th1.LBox:=listBox1; th2.LBox:=listBox2; th1.Resume;//开始执⾏ th2.Resume; end; end. 由上例可见,当多个线程同时修改⼀个公⽤变量时,会产⽣冲突,所以我们要设法防⽌它,这样我们开发的多线程应⽤才能够稳定地运⾏。

Delphi多线程介绍,以及线程类TThread分析

Delphi多线程介绍,以及线程类TThread分析

Delphi 多线程介绍,以及线程类TThread 分析Delphi 中有一个线程类TThread 用来实现多线程编程TThread 类的几个成员作一简单介绍,再说明一下Execute 的实现和Synchronize 的用法就完了。

然而这并不是多线程编程的全部,我写此文的目的在于对此作一个补充。

线程 本质上是进程中一段 并发 运行的代码。

一个进程至少有一个线程,即所谓的主线程。

同时还可以有多个子线程。

当一个进程中用到超过一个线程时,就是所谓的“多线程”。

1、CreateThread 、long _beginthread 、BeginThread 介绍 用Windows API 来创建线程,API 函数 CreateThread 的定义原型:1 2 3 4 5 6 7 8 HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, //线程属性(用于在NT 下进行线程的安全属性设置,在9X 下无效) DWORD dwStackSize, //堆栈大小 LPTHREAD_START_ROUTINE lpStartAddress, //起始地址,即线程函数的入口,直至线程函数结束,线程结束 LPVOID lpParameter, //参数 DWORD dwCreationFlags, //创建标志(用于设置线程创建时的状态) LPDWORD lpThreadId //线程ID ); //最后返回线程HandleCreateThread 参数很多,而且在C Runtime Library 里提供了一个通用的线程函数(理论上可以在任何支持线程的OS 中使用):1 unsigned long _beginthread(void (_USERENTRY *__start)(void *), unsigned __stksize, void *__arg);Delphi 也提供了一个相同功能的类似函数:1 f unction BeginThread(2 3 4 5 6 7 8 SecurityAttributes: Pointer;StackSize: LongWord;ThreadFunc: TThreadFunc;Parameter: Pointer;CreationFlags: LongWord;var ThreadId: LongWord): Integer;这三个函数的功能是基本相同的,它们都是将线程函数中的代码放到一个独立的线程中执行。

DELPHI多线程

DELPHI多线程
注意一下,在同一个线程对象里,如果两次构造,将产生两个独立的线程,不但运行是独立的,而且使用线程的局部变量也是独立的。但这里为了简化问题,还是建立了两个独立的线程对象,而且两个循环数
是不同的,在并行运算时容易判断出是两个不同的程序在运行。
假定我们给两个线程对象起的名字是:
WIN 98/NT/2000/XP 是个多任务操作系统,也就是:一个进程可以划分为多个线程,每个线程轮流占用CPU 运行时间和资源,或者说,把CPU 时间划成片,每个片分给不同的线程,这样,每个线程轮流的“挂起”和“唤醒”,由于时间片很小,给人的感觉是同时运行的。
多线程带来如下好处:(自己阅读)
例如Tthread 对象方法内部调用API 函数的时候,一般使用推荐的默认值,但需要更精细的控制时,就可以直接使用API 函数。
其实,Tthread 对象方法已经受到了大多数程序设计者的认可,比如,原来VB是不具备直接处理多线程的能力的,但是,现在就宣称,它具备了简单处理多线程问题的能力,这就很说明问题。
CREATE_SUSPENDED 创建一个挂起的线程;
0 创建后立即激活。
书上有这个函数应用的十分清晰的例子,可以自己阅读。
一般并不推荐使用 CreateTheard函数,而推荐使用RTL 库里的System单元中定义的 BeginTheard函数,因为这除了能创建一个线程和一个入口函数以外,还增加了几项保护措施,具体的请参阅书上的第10页说明。
mymath1
mymath2
这样在Unit1,应该作如下声明:
implementation
{$R *.DFM}
uses unit2,unit3;
var thread1:mymath1;

WIN32下DELPHI中的多线程

WIN32下DELPHI中的多线程

WIN32下DELPHI中的多线程【深入VCL源码】(一)线程的基础知识线程的组成。

线程有两部分组成。

1、一个是线程的内核对象,操作系统用它来对线程实施管理。

内核对象也是系统用来存放线程统计信息的地方。

2、另一个是线程堆栈,它用于维护线程在执行代码时需要的所有函数参数和局部变量。

进程从来不执行任何东西,它只是线程的容器。

线程总是在某个进程环境中创建的,而且它的整个寿命期都在该进程中。

这意味着线程在它的进程地址空间中执行代码,并且在进程的地址空间中对数据进行操作。

因此,如果在单进程环境中,你有两个或多个线程正在运行,那么这两个线程将共享单个地址空间。

这些线程能够执行相同的代码,对相同的数据进行操作。

这些线程还能共享内核对象句柄,因为句柄表依赖于每个进程而不是每个线程存在。

线程是一种操作系统对象,它表示在进程中代码的一条执行路径。

在每一个Wi n32的应用程序中都至少有一个线程,它通常被称为主线程或默认线程。

在应用程序中也可以自由地创建别的线程去执行其他任务。

线程技术使不同的代码可以同时运行。

当然,只有在多C P U的计算机上,多个线程才能够真正地同时运行。

在单个CPU上,由于操作系统把C P U的时间分成很短的片段分配给每个线程,这样给人的感觉好像是多个线程真的同时运行,他们只是“看起来”同时在运行。

Win32是一种抢占式操作系统,操作系统负责管理哪个线程在什么时候执行。

如果当线程1暂停执行时,线程2才有机会获得C P U时间,我们说线程1是抢占的。

如果某个线程的代码陷入死循环,这并不可怕,操作系统仍会安排时间给其他线程。

创建一个线程注意:每个线程必须拥有一个进入点函数,线程从这个进入点开始运行。

线程函数可以使用任何合法的名字。

可以给线程函数传递单个参数,参数的含义由你自己定义。

线程函数必须由一个返回值,它将成为该线程的退出代码。

线程函数应该尽可能的使用函数参数和局部变量。

线程函数类似下面的样子(Object Pascal)://注意最后的stdcall,后面我会描述一些有用的东西function MyThread(info : Pointer):DWORD; stdcall;vari : integer;beginfor i := 0 to Pinfo(info)^.count-1 doForm1.Canvas.TextOut(Pinfo(info)^.x,Pinfo(info)^.y,inttostr(i));Result := 0;end;上面的的代码功能很简单,你可以在程序中直接调用,例如这样:typeTinfo = recordcount : integer;x : integer;y : integer;end;Pinfo= ^Tinfo;...procedure TForm1.Button4Click(Sender: TObject);varppi : Pinfo;beginppi :=AllocMem(sizeof(tinfo));ppi^.count := 1000000;ppi^.x := 100;ppi^.y := 400;MyThread(ppi);end;当你在一个窗口中用这样的方式调用时,你会发现在执行的过程中,你将无法在窗口上进行其他操作,因为它工作于你程序的主线程之中。

Delphi线程基础知识

Delphi线程基础知识

Delphi线程基础知识Delphi线程基础知识时间:2011-5-24来源:yang 作者: peng点击: 58次Delphi线程基础知识一概述Delphi提供了好几个对象以方便进行多线程编程。

多线程应用程序有以下三个方面的功能:避免性能瓶颈:单线程应用程序在进行比较慢的操作如磁盘读写时,CPU必须停下来等待,直到该操作执行完毕。

而多线程应用程序则可以在一个线程进行磁盘读写等时,继续执行另一个单独的线程;组织应用程序的行为:通常,一个程序的行为可以组织成几个功能相互独立的平行的处理过程。

将这些独立的处理组织成单独的几个线程,就可以同时启动这几个处理过程。

还可以使用线程的优先级来控制哪些任务获得更多的CPU时间。

二定义线程对象在大多数情况下,可以使用线程对象来代表一个线程。

线程对象封装了常用的线程操作,从而简化了线程编程。

注意:线程对象不允许控制线程的安全属性和栈的大小。

如果你确实要控制这些东西,那只能够使用BeginThread函数来创建线程了。

要在程序中使用线程对象,必须自定义一个从TThread派生的类。

可以使用向导来帮助创建,生成的代码如下:unit Unit2;interfaceusesClasses;typeTMyThread = class(TThread)private{ Private declarations }protectedprocedure Execute; override;end;implementation{ TMyThread }procedure TMyThread.Execute;begin{ Place thread code here }end;end.在上面自动生成的代码中,可以:·加入自己的线程初始化代码(可选)·将线程执行的操作写入Execute函数·加入自己的线程初始化代码(可选)三初始化线程如果你需要为你新创建的线程类加入初始化代码,就必须重写Create方法。

DELPHI中多线程研究

DELPHI中多线程研究

DELPHI中多线程研究作者:李若重来源:《中国新通信》2013年第04期一、进程与线程一个操作系统有多个进程在同时进行,而一个进程又会有多个线程在同时进行,每个线程都有自己的执行状态和独立的上下文结构(保存在线程控制块中)及执行栈(用户栈、系统栈),同一进程中的线程通过各种同步机制(如临界区、事件、互斥量、信号灯等)来实现对共享资源的访问。

二、Delphi中的多线程机制Delphi编译环境的核心是可以直接调用几乎所有的Windows API函数。

通常是通过过程调用一系列外部模块来实现,其最大的优点是利用面向对象的技术支持。

通过对Delphi中类实现的源代码分析,可以从中了解到类的构造过程及功能的实现,以便更有效的利用其提供的线程类完成多线程程序设计。

Delphi中多线程技术的实现是通过TThread类来封装Windows API的有关线程操作的编程接口。

TThread类继承自TOb-ject,除继承父类的成员外还定义了一些属性和方法,主要分为线程对象属性、线程对象方法、线程对象事件处理三类:(1)线程对象属性(Properties):FatalException异常处理对象FreeOnTerminate布尔量,决定线程结束时是否清除Handle线程句柄Priority线程优先级ReturnValue线程返回值Suspended布尔量,判断线程是否已挂起Terminated布尔量,判断线程是否需要结束ThreadID线程全局唯一的标记(2)线程对象方法(Methods):AfterConstruction对象创建后运行,重载自父类Create创建线程对象构造器Destroy释放线程对象析构器DoTerminate释放线程前调用用户的清除例程Execute线程执行,虚类函数,子类需重载Resume使线程重新执行Suspend挂起运行线程Synchronize线程间操作同步Terminate置线程终止标记WaitFor等待线程结束(其它继承自父类TObject对象)(3)线程对象事件处理(Events):onTerminate线程结束前调用的方法指针2.1线程的创建、运行和终止线程类调用继承自父类的构造器(con-structor Create)创建对象实例,接着调用线程管理例程的Addthread全局例程将全局线程记数值加1,随后即通过线程管理例程中的BeginThread 全局例程调用Windows API函数Createthread,以参数形式向其传入线程运行主函数Threadproc。

delphi中的线程类详解

delphi中的线程类详解

delphi中的线程类Delphi中有一个线程类TThread是用来实现多线程编程的,这个绝大多数Delphi书藉都有说到,但基本上都是对TThread类的几个成员作一简单介绍,再说明一下Execute的实现和Synchronize 的用法就完了。

然而这并不是多线程编程的全部,我写此文的目的在于对此作一个补充。

线程本质上是进程中一段并发运行的代码。

一个进程至少有一个线程,即所谓的主线程。

同时还可以有多个子线程。

当一个进程中用到超过一个线程时,就是所谓的“多线程”。

那么这个所谓的“一段代码”是如何定义的呢?其实就是一个函数或过程(对Delphi而言)。

如果用Windows API来创建线程的话,是通过一个叫做CreateThread的API函数来实现的,它的定义为:HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes,DWORD dwStackSize,LPTHREAD_START_ROUTINE lpStartAddress,LPVOID lpParameter,DWORD dwCreationFlags,LPDWORD lpThreadId);其各参数如它们的名称所说,分别是:线程属性(用于在NT下进行线程的安全属性设置,在9X下无效),堆栈大小,起始地址,参数,创建标志(用于设置线程创建时的状态),线程ID,最后返回线程Handle。

其中的起始地址就是线程函数的入口,直至线程函数结束,线程也就结束了。

整个线程的执行过程如下图所示:因为CreateThread参数很多,而且是Windows的API,所以在C Runtime Library里提供了一个通用的线程函数(理论上可以在任何支持线程的OS中使用):unsigned long _beginthread(void (_USERENTRY *__start)(void *), unsigned __stksize, void *__arg);Delphi也提供了一个相同功能的类似函数:function BeginThread(SecurityAttributes: Pointer; StackSize: LongWord; ThreadFunc: TThreadFunc; Parameter: Pointer; CreationFlags: LongWord; var ThreadId: LongWord): Integer;这三个函数的功能是基本相同的,它们都是将线程函数中的代码放到一个独立的线程中执行。

多线程的使用(Delphi)[转]

多线程的使用(Delphi)[转]
private
pt:TPainterThread;
public
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
try
pixels[x,y]:=clblue;
finally
unlock;
end;
end;
until Terminated;
end;
end.
form调用TThread方法举例:
在form上建立两个button组件.
public
{ Public declarations }
end;
var
AnimWindow: TAnimWindow;
run: Boolean;
implementation
{$R *.dfm}
procedure TAnimThread.Execute;
举例:
建立TThread类代码
unit Unit2;
interface
uses
Classes;
type
TPainterThread = class(TThread)
private
{ Private declarations }
protected
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

delphi多线程编程

delphi多线程编程

delphi多线程编程delphi多线程编程━━━━━━━━━━━━━━━━━━━━━━━━━━本章描述Windows 95与Windows NT的基于线程的多任务设计。

15.1 线程我们知道,Windows 95支持两种形式的多任务。

第一种类型是基于进程的机制,这也是Windows从一开始就支持的多处理类型。

进程本质上是指正在执行着的程序,在基于进程的多任务环境下,两个至多个进程可以并发地执行。

第二种类型是基于线程(thread)的机制,基于线程的多任务对于多数Windows用户和程序员而言是一个崭新的概念,因为Windows 95以前的Windows版本不能支持线程的概念(Windows NT除外)。

线程是指进程中的一个执行流。

多个线程可以并发地运行于同一个进程中。

在Windows 95/98/NT中,每一个进程拥有至少一个线程,允许同时执行两个或多个线程。

并由我们的程序控制它们。

基于线程的多任务允许同一程序的不同部分(线程)并发地执行。

这样,程序员就能够写出非常高效的程序,因为程序员能够定义执行线程并管理程序的执行方式,能够完全地控制程序片段的执行。

例如,可以在一个程序中指定一个线程执行文件排序工作,指定另外一个线程负责收集来自某个远程资源的信息,指定又一线程完成用户输入的工作。

因为处于多线程多任务环境,每一线程都能够并发地执行,这样就能充分地利用CPU时间。

通常,多线程处理使程序运行速度减慢,除非我们有多线程CPU,以及可以在处理器中分离线程的操作系统。

基于线程的多任务使得同步功能特征显得更为重要。

既然多个线程(及进程)可以并发执行,那么必须适当地协调线程间的执行顺序以使其能同步访问共享资源与内存,从而使程序编写起来显得更加复杂。

Windows 95增加了一个完整的子系统以支持同步机制,其中的一些关键特征将在本章后进一步地讨论。

15.2 线程类所有进程至少都拥有一个执行线程,为了讨论方便,我们称该执行线程为主线程。

delphi 线程用法

delphi 线程用法

delphi 线程用法在 Delphi 中,线程可以通过两种方式来实现:使用 RTL 库的 System 单元中定义的标准例程 BeginThread;使用 Delphi 的 VCL 库中的 TThread 对象。

**使用标准例程 BeginThread:**此例程完整封装了 Win32 的 CreateThread 函数,是一个带有异常处理的标准 Pascal 函数,几乎可以处理所有自身的异常,相对于使用 Win32 的 CreateThread 函数,其安全系数大大增强。

其完整声明如下:```pascalfunction BeginThread( SecurityAttributes: Pointer; StackSize: Integer; ThreadFunc: TThreadFunc; Parameter: Pointer; CreationFlags: Integer; var ThreadId: Integer): Integer;```- SecurityAttributes 参数是一个指向 SECURITY_ATTRIBUTES 结构的指针,用于设置线程的访问权限,nil 表示为默认的安全属性。

- StackSize 参数用于设置分配给线程的栈空间大小,0 表示用默认值。

- ThreadFunc 用于指定一个函数,该函数在线程创建后开始执行代码时调用。

- Parameter 参数传递给 ThreadFunc 参数所指定的函数,常为 nil,或者设为一个32 位的指针,指向一个数据结构。

- CreationFlags 参数用于指定线程创建后是不是立即执行,0 表示立即执行,CREATE_SUSPENDED 表示处于挂起状态。

- ThreadId 参数表示为每个线程唯一的识别号,当 BeginThread 函数返回后,此参数就是线程的识别号。

- 返回值为该线程的句柄,如果为0,表示线程没有创建成功,可以调用 Windows 的GetLastError 函数分析错误的原因。

DELPHI 线程池 简解

DELPHI 线程池 简解
procedure GetRequest(out Request: TWorkItem);
// 清除死线程
procedure KillDeadThreads;
public
constructor Create(AOwner: TComponent); override;
TProcessorThread) of object;
// 线程结束时触发的事件
TProcessorThreadFinalizing = procedure(Sender: TThreadsPool; aThread:
TProcessorThread) of object;
tcsProcessed, tcsTerminating, tcsCheckingDown);
// 工作线程仅用于线程池内, 不要直接创建并调用它。
TProcessorThread = class(TThread)
private
// 创建线程时临时的Event对象, 阻塞线程直到初始化完成
function TextForLog: string; virtual;
end;
type
TThreadsPool = class;
//线程状态
TThreadState = (tcsInitializing, tcsWaiting, tcsGetting, tcsProcessing,
hThreadTerminated: THandle;
uProcessingStart: DWORD;
// 开始等待的时间, 通过 GetTickCount 取得。
uWaitingStart: DWORD;
// 计算平均工作时间

Delphi Thread 线程代码分析

Delphi Thread 线程代码分析
程函数的入口,直至线程函数结束,线程也就结束了。
因为CreateThread参数很多,而且是Windows的API,所以在C Runtime Library里提供了一个通用的线程函数(理论上
可以在任何支持线程的OS中使用):
unsigned long _beginthread(void (_USERENTRY *__start)(void *), unsigned __stksize, void *__arg);
CreationFlags: LongWord;
var ThreadId: LongWord
): Integer;
这三个函数的功能是基本相同的,它们都是将线程函数中的代码放到一个独立的线程中执行。线程函数与一般函数的
最大不同在于,线程函数一启动,这三个线程启动函数就返回了,主线程继续向下执行,而线程函数在一个独立的线
虽然说用API或RTL(Runtime Library)已经可以很方便地进行多线程编程了,但是还是需要进行较多的细节处理,为此
Delphi在Classes单元中对线程作了一个较好的封装,这就是VCL的线程类:TThread
使用这个类也很简单,大多数的Delphi书籍都有说,基本用法是:先从TThread派生一个自己的线程类(因为TThread
DWORD dwCreationFlags,
LPDWORD lpThreadId
);
其各参数如它们的名称所说,分别是:线程属性(用于在NT下进行线程的安全属性设置,在9X下无效),堆栈大小,
起始地址,参数,创建标志(用于设置线程创建时的状态),线程ID,最后返回线程Handle。其中的起始地址就是线
FHandle := BeginThread(nil, 0, @ThreadProc, Pointer(Self), CREATE_SUSPENDED, FThreadID);

delphi线程详细教程简单到复杂

delphi线程详细教程简单到复杂

delphi线程详细教程简单到复杂1. delphi之多线程编程(⼀)(2)delphi之多线程编程(⼀)本⽂的内容取⾃⽹络,并重新加以整理,在此留存仅仅是⽅便⾃⼰学习和查阅。

所有代码均亲⾃测试delphi7下测试有效。

图⽚均为⾃⼰制作。

多线程应该是编程⼯作者的基础技能, 但这个基础我从来没学过,所以仅仅是看上去会⼀些,明⽩了2+2的时候,其实我还不知道1+1。

开始本应该是⼀篇洋洋洒洒的⽂字, 不过我还是提倡先做起来, 在尝试中去理解.先试试这个:procedure TForm1.Button1Click(Sender: TObject);vari: Integer;beginfor i := 0to500000dobeginCanvas.TextOut(10, 10, IntToStr(i));end;end;上⾯程序运⾏时, 我们的窗体基本是"死" 的, 可以在你在程序运⾏期间拖动窗体试试...Delphi 为我们提供了⼀个简单的办法(Application.ProcessMessages)来解决这个问题:procedure TForm1.Button1Click(Sender: TObject);vari: Integer;beginfor i := 0to500000dobeginCanvas.TextOut(10, 10, IntToStr(i));Application.ProcessMessages;end;end;这个Application.ProcessMessages; ⼀般⽤在⽐较费时的循环中, 它会检查并先处理消息队列中的其他消息.但这算不上多线程, 譬如: 运⾏中你拖动窗体, 循环会暂停下来...在使⽤多线程以前, 让我们先简单修改⼀下程序:function MyFun: Integer;varfor i := 0to500000dobeginForm1.Canvas.Lock;Form1.Canvas.TextOut(10, 10, IntToStr(i));Form1.Canvas.Unlock;end;Result := 0;end;procedure TForm1.Button1Click(Sender: TObject);beginMyFun;end;细数上⾯程序的变化:1、⾸先这还不是多线程的, 也会让窗体假"死" ⼀会;2、把执⾏代码写在了⼀个函数⾥, 但这个函数不属于TForm1 的⽅法, 所以使⽤Canvas 是必须冠以名称(Form1);3、既然是个函数, (不管是否必要)都应该有返回值;4、使⽤了500001 次Lock 和Unlock.Canvas.Lock 好⽐在说: Canvas(绘图表⾯)正忙着呢, 其他想⽤Canvas 的等会;Canvas.Unlock : ⽤完了, 解锁!在Canvas 中使⽤Lock 和Unlock 是个好习惯, 在不使⽤多线程的情况下这⽆所谓, 但保不准哪天程序会扩展为多线程的; 我们现在学习多线程, 当然应该⽤.在Delphi 中使⽤多线程有两种⽅法: 调⽤API、使⽤TThread 类; 使⽤API 的代码更简单. function MyFun(p: Pointer): Integer; stdcall;vari: Integer;beginfor i := 0to500000dobeginForm1.Canvas.Lock;Form1.Canvas.TextOut(10, 10, IntToStr(i));Form1.Canvas.Unlock;end;Result := 0;end;procedure TForm1.Button1Click(Sender: TObject);ID: THandle;beginCreateThread(nil, 0, @MyFun, nil, 0, ID);end;代码分析:CreateThread ⼀个线程后, 算上原来的主线程, 这样程序就有两个线程、是标准的多线程了; CreateThread 第三个参数是函数指针, 新线程建⽴后将⽴即执⾏该函数, 函数执⾏完毕, 系统将销毁此线程从⽽结束多线程的故事.CreateThread 要使⽤的函数是系统级别的, 不能是某个类(譬如: TForm1)的⽅法, 并且有严格的格式(参数、返回值)要求, 不管你暂时是不是需要都必须按格式来;因为是系统级调⽤, 还要缀上stdcall, stdcall 是协调参数顺序的, 虽然这⾥只有⼀个参数没有顺序可⾔,但这是使⽤系统函数的惯例.CreateThread 还需要⼀个var 参数来接受新建线程的ID, 尽管暂时没⽤, 但这也是格式; 其他参数以后再说吧.这样⼀个最简单的多线程程序就出来了, 咱们再⽤TThread 类实现⼀次typeTMyThread = class(TThread)protectedprocedure Execute; override;end;procedure TMyThread.Execute;vari: Integer;beginFreeOnTerminate := True; {这可以让线程执⾏完毕后随即释放}for i := 0to500000dobeginForm1.Canvas.Lock;Form1.Canvas.TextOut(10, 10, IntToStr(i));Form1.Canvas.Unlock;end;end;procedure TForm1.Button1Click(Sender: TObject);beginTMyThread.Create(False);end;继承TThread 主要就是实现抽象⽅法Execute(把我们的代码写在⾥⾯), 等我们的TMyThread 实例化后, ⾸先就会执⾏Execute ⽅法中的代码.按常规我们⼀般这样去实例化:procedure TForm1.Button1Click(Sender: TObject);varMyThread: TMyThread;beginMyThread := TMyThread.Create(False);end;因为MyThread 变量在这⾥毫⽆⽤处(并且编译器还有提⽰), 所以不如直接写做TMyThread.Create (False);我们还可以轻松解决⼀个问题, 如果: TMyThread.Create(True) ?这样线程建⽴后就不会⽴即调⽤Execute, 可以在需要的时候再⽤Resume ⽅法执⾏线程, 譬如: procedureTForm1.Button1Click(Sender: TObject);varMyThread: TMyThread;beginMyThread := TMyThread.Create(True);MyThread.Resume;end;//可简化为:procedure TForm1.Button1Click(Sender: TObject);beginwith TMyThread.Create(True) do Resume;end;⼀、⼊门㈠、function CreateThread(lpThreadAttributes: Pointer; {安全设置}dwStackSize: DWORD; {堆栈⼤⼩}lpStartAddress: TFNThreadStartRoutine; {⼊⼝函数}lpParameter: Pointer; {函数参数}dwCreationFlags: DWORD; {启动选项}var lpThreadId: DWORD {输出线程 ID }): THandle; stdcall; {返回线程句柄}在Windows 上建⽴⼀个线程, 离不开CreateThread 函数;TThread.Create 就是先调⽤了BeginThread (Delphi ⾃定义的), BeginThread ⼜调⽤的Create Thread.既然有建⽴, 就该有释放, CreateThread 对应的释放函数是: ExitThread, 譬如下⾯代码: procedureTForm1.Button1Click(Sender: TObject);beginExitThread(0); {此句即可退出当前程序, 但不建议这样使⽤}end;代码注释:当前程序是⼀个进程, 进程只是⼀个⼯作环境, 线程是⼯作者;每个进程都会有⼀个启动线程(或叫主线程), 也就是说: 我们之前⼤量的编码都是写给这个主线程的;上⾯的ExitThread(0); 就是退出这个主线程;系统不允许⼀个没有线程的进程存在, 所以程序就退出了.另外: ExitThread 函数的参数是⼀个退出码, 这个退出码是给之后的其他函数⽤的, 这⾥随便给个⽆符号整数即可.或许你会说: 这个ExitThread 挺好⽤的; 其实不管是⽤API 还是⽤TThread 类写多线程, 我们很少⽤到它; 因为:1、假如直接使⽤API 的CreateThread, 它执⾏完⼊⼝函数后会⾃动退出, ⽆需ExitThread;2、⽤TThread 类建⽴的线程⼜绝不能使⽤ExitThread 退出; 因为使⽤TThread 建⽴线程时会同时分配更多资源(譬如你⾃定义的成员、还有它的祖先类(TObject)分配的资源等等), 如果⽤ExitThread给草草退出了, 这些资源将得不到释放⽽导致内存泄露. 尽管Delphi 提供了EndThread(其内部调⽤ExitThread), 这也不需要我们⼿动操作(假如⾮要⼿动操作也是件很⿇烦的事情, 因为很多时候你不知道线程是什么时候执⾏完毕的).除了CreateThread, 还有⼀个CreateRemoteThread, 可在其他进程中建⽴线程, 这不应该是现在学习的重点;现在先集中精⼒把CreateThread 的参数搞彻底.倒着来吧, 先谈谈CreateThread 将要返回的"线程句柄"."句柄" 类似指针, 但通过指针可读写对象, 通过句柄只是使⽤对象;有句柄的对象⼀般都是系统级别的对象(或叫内核对象); 之所以给我们的是句柄⽽不是指针, ⽬的只有⼀个: "安全";貌似通过句柄能做很多事情, 但⼀般把句柄提交到某个函数(⼀般是系统函数)后, 我们也就到此为⽌很难了解更多了; 事实上是系统并不相信我们.不管是指针还是句柄, 都不过是内存中的⼀⼩块数据(⼀般⽤结构描述), 微软并没有公开句柄的结构细节,猜⼀下它应该包括: 真实的指针地址、访问权限设置、引⽤计数等等.既然CreateThread 可以返回⼀个句柄, 说明线程属于"内核对象".实际上不管线程属于哪个进程, 它们在系统的怀抱中是平等的; 在优先级(后⾯详谈)相同的情况下, 系统会在相同的时间间隔内来运⾏⼀下每个线程, 不过这个间隔很⼩很⼩, 以⾄于让我们误以为程序是在不间断地运⾏.这时你应该有⼀个疑问: 系统在去执⾏其他线程的时候, 是怎么记住前⼀个线程的数据状态的?有这样⼀个结构TContext, 它基本上是⼀个CPU 寄存器的集合, 线程是数据就是通过这个结构切换的,我们也可以通过GetThreadContext 函数读取寄存器看看.附上这个结构TContext(或叫: CONTEXT、_CONTEXT) 的定义:PContext = ^TContext;_CONTEXT = recordContextFlags: DWORD;Dr0: DWORD;Dr1: DWORD;Dr3: DWORD;Dr6: DWORD;Dr7: DWORD;FloatSave: TFloatingSaveArea; SegGs: DWORD;SegFs: DWORD;SegEs: DWORD;SegDs: DWORD;Edi: DWORD;Esi: DWORD;Ebx: DWORD;Edx: DWORD;Ecx: DWORD;Eax: DWORD;Ebp: DWORD;Eip: DWORD;SegCs: DWORD;EFlags: DWORD;Esp: DWORD;SegSs: DWORD;end;CreateThread 的最后⼀个参数是"线程的ID";既然可以返回句柄, 为什么还要输出这个ID? 现在我知道的是:1、线程的ID 是唯⼀的; ⽽句柄可能不只⼀个, 譬如可以⽤GetCurrentThread 获取⼀个伪句柄、可以⽤DuplicateHandle 复制⼀个句柄等等.2、ID ⽐句柄更轻便.在主线程中GetCurrentThreadId、MainThreadID、MainInstance 获取的都是主线程的ID.㈡、启动选项function CreateThread(lpThreadAttributes: Pointer;dwStackSize: DWORD;lpStartAddress: TFNThreadStartRoutine;lpParameter: Pointer;dwCreationFlags: DWORD; {启动选项}var lpThreadId: DWORD): THandle; stdcall;0: 线程建⽴后⽴即执⾏⼊⼝函数;CREATE_SUSPENDED: 线程建⽴后会挂起等待.可⽤ResumeThread 函数是恢复线程的运⾏; 可⽤SuspendThread 再次挂起线程.这两个函数的参数都是线程句柄, 返回值是执⾏前的挂起计数.什么是挂起计数?SuspendThread 会给这个数+1; ResumeThread 会给这个数-1; 但这个数最⼩是0.当这个数= 0 时, 线程会运⾏; > 0 时会挂起.如果被SuspendThread 多次, 同样需要ResumeThread 多次才能恢复线程的运⾏.在下⾯的例⼦中, 有新线程不断给⼀个全局变量赋随机值;同时窗体上的Timer 控件每隔1/10 秒就把这个变量写在窗体标题;在这个过程中演⽰了ResumeThread、SuspendThread 两个函数.//上⾯图⽚中演⽰的代码。

Delphi多线程学习(5):互斥量Mutex

Delphi多线程学习(5):互斥量Mutex

Delphi多线程学习(5):互斥量Mutex 互斥量是系统内核对象,谁拥有就谁执⾏。

它与临界区⼯作很类似。

不同处在于:1、互斥量可以跨进程边界同步线程。

2、可以给互斥量取个名字,通过引⽤互斥量的名字来使⽤⼀个已知的互斥量对象。

使⽤互斥量之类的对象需要反复调⽤系统内核,期间需要进⾏进程上下⽂转换和控制级别转换,⼤概需要耗费400到600个时间周期。

⼜是图书馆的⽐喻,现在是搞⼀个锁,把钥匙(互斥量句柄)交给管理员(操作系统),每⼀个⼈(线程)想要借书的时候,都要向管理员拿钥匙。

当有⼈在使⽤的时候,另⼀⼈必须等待,等到钥匙有空的时候(互斥量进⼊信号状态),才能拿到钥匙(拥有了句柄)办理借书业务(此时互斥量进⼊⾮信号状态直到办完业务)。

使⽤互斥量的步骤:1、声明⼀个全局的互斥量句柄变量(var hMutex: THandle;);2、创建互斥量:CreateMutex(lpMutexAttributes: PSecurityAttributes;bInitialOwner: BOOL;lpName: PWideChar ): THandle;(lpMutexAttributes参数:指向TSecurityAttributes的指针,安全属性,⼀般⽤缺省安全属性nil;bInitialOwer参数:表⽰创建的互斥量线程是否是互斥量的属主,如果该参数为False互斥量就没属主,⼀般来讲应设为False,否则如果设为True的话,要当主线程结束其他线程才成为它的属主才能运⾏;lpName参数:是互斥量的名字,若打算取名的话,则传⼊nil。

)3、⽤等待函数控制线程进⼊同步代码块:WaitForSingleObject(hHandle:THandel;dwMilliseconds:DWORD):DWORD;(hHandel参数:是对象的句柄,不同对象信号状态不同,对于互斥量⽽⾔当没有线程占有时,互斥量就时⼊信号状态;dwMilliseconds参数:可以是⼀个时间段,可以是0表⽰仅检查对象状态后⽴即返回,可以是INFINITE值表⽰函数⼀直等待直到对象进⼊信号状态;返回值常量如下:WAIT_ABANDONED指定的对象是互斥量对象,拥有这个互斥量对象的线程在没有释放互斥量之前就已经终⽌,称作废弃互斥量,此时该互斥量归调⽤线程所拥有,并把这个互斥量设为⾮信号状态;WAIT_OBJECT_0指定对象的进⼊信号状态;WAIT_TIMEOUT等待时间已过,对象状态依然是⽆信号状态)4、执⾏线程运⾏代码。

Delphi多线程实例解析

Delphi多线程实例解析

[摘要] 我们都知道当前的Windows操作系统是一个“多线程”操作系统。

那么什么是线程呢?线程就是进程中的一个实体,它和进程一样能够独立的执行控制,由操作系统负责调度,其区别就在于线程没有独立的存储空间,而是与同属于一个进程的其他线程共享一个存储空间,这使得多线程之间的通信较进程简单,并且多线程的执行都是并发而且是相互独立的。

[关键字] delphi windows操作系统【eNet硅谷动力专稿】我们都知道当前的Windows操作系统是一个“多线程”操作系统。

那么什么是线程呢?线程就是进程中的一个实体,它和进程一样能够独立的执行控制,由操作系统负责调度,其区别就在于线程没有独立的存储空间,而是与同属于一个进程的其他线程共享一个存储空间,这使得多线程之间的通信较进程简单,并且多线程的执行都是并发而且是相互独立的。

为了运行所有这些线程,操作系统为每个独立线程安排一些CPU 时间,操作系统以轮转方式向线程提供时间片,这就给人一种假象,好象这些线程都在同时运行。

CreatThread函数是用于创建一个线程,CreatThread函数原形及参数说明如下:HANDLE CreatThread(LPSECURITY_ATTRIBUTES lpThreadAttributes,DWORD dwStackSize,LPTHREAD_START_ROUTINE lpStartAddress,LPVOID lpParameter,DWORD dwCreationFlags,LPDWORD lpThreadld),参数说明:pThreadAttributes 如果为NULL,该线程使用默认安全属性。

如果希望所有子进程能够继承该线程对象的句柄,必须将他的bInheritHand成员初始化为True。

dwStackSize 设定线程堆栈的地址空间。

如果非0,函数将所有的存储器保留并分配给线的程堆栈。

lpStartAddress 线程函数的地址。

DELPHI下的多线程程序设计

DELPHI下的多线程程序设计

DELPHI下的多线程程序设计我们知道, win95 或 winNT 都是“多线程”的操作系统,在 DELPHI 2.0 中,我们可以充分利用这一特性,编写出“多线程”的应用程序。

对以往在DOS或16位windows下写程序的人来说,多线程”仍然是陌生的,但如同以前我们从DOS下的单任务过渡到 windows3 . 1下的多任务,如今我们又必须过渡到“多线程”领域,毕竟计算机时代是在不断发展的。

不过,幸运的是,在 DELPHI2 .0 下进行多线程程序设计并不需要我们去学习庞大的 WIN32API 函数,我们可以利用 DELPHI 下标准的多线程类TThread 来完成我们的工作。

TThread是一个abstract (抽象)类,也就是说,并不需要根据TThread来声明变量(而且根据 TThread 声明的变量也是完全无用) ,我们要做的是把 TThread 作为基类,用继承的形式来生成子类。

实际上,根据 TThread 来写多线程应用程序是非常容易的。

下面就是一个基本的继承 TThread 生成的多线程类。

QuerThrd.PasunitQuerThrd ;interfaceusesClasses, DBTables;typeTQueryThread K class (TThread)privatefQuery:tQuery;protectedproced u reExecute; override;publicconstructorCreate( Suspended: Boolean;Query:TQuery);end;implementationconstructorTQueryThread.Create(Suspended:Boolean;Query:TQuery);begininheritedCreate ( Suspended);fQuery : K Query;Free On Term in ate: K Trueend;procedureTQueryThread. Execute;beginfQuery.Open;en d;en d.在上面这个简单的例子中,我们构造了一个TThread的子类TQuery Thread,用于在后台执行数据库查询。

Delphi的线程类

Delphi的线程类

前言由于项目的关系,所以和线程的接触颇多,常常遇到问题,常常看TThread的代码,又常常想一些办法来解决遇到的问题,所以就有了这篇文章。

正文我们常有工作线程和主线程之分,工作线程负责作一些后台操作,比如接收邮件;主线程负责界面上的一些显示。

工作线程的好处在某些时候是不言而喻的,你的主界面可以响应任何操作,而背后的线程却在默默地工作。

VCL中,工作线程执行在Execute方法中,你必须从TThread继承一个类并覆盖Execute方法,在这个方法中,所有代码都是在另一个线程中执行的,除此之外,你的线程类的其他方法都在主线程执行,包括构造方法,析构方法,Resume等,很多人常常忽略了这一点。

最简单的一个线程类如下:TMyThread = class(TThread)protectedprocedure Execute; override;end;在Execute中的代码,有一个技术要点,如果你的代码执行时间很短,像这样,Sleep(1000),那没有关系;如果是这样Sleep(10000),10秒,那么你就不能直接这样写了,须把这10秒拆分成10个1秒,然后判断Terminated属性,像下面这样:procedure TMyThread.Execute;vari: Integer;beginfor i := 0 to 9 doif not Terminated thenSleep(1000)elseBreak;end;这样写有什么好处呢,想想你要关闭程序,在关闭的时候调用MyThread.Free,这个时候线程并没有马上结束,它调用WaitFor,等待Execute执行完后才能释放。

你的程序就必须等10秒以后才能关闭,受得了吗。

如果像上面那样写,在程序关闭时,调用Free之后,它顶多再等一秒就会关闭。

为什么?答案得去线程类的Destroy中找,它会先调用Terminate方法,在这个方法里面它把Terminated设为True(仅此而已,很多人以为是结束线程,其实不是)。

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

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

delphi线程教学第⼀节:初识多线程第⼀节:初识多线程1.为什么要学习多线程编程?多线程(多个线程同时运⾏)编程,亦可称之为异步编程。

有了多线程,主界⾯才不会因为耗时代码⽽造成“假死“状态。

有了多线程,才能使多个任务同时执⾏,最⼤化利⽤CPU资源,提⾼效率。

在安卓编程中,要求必须是多线程,主界⾯中的代码只要耗时⼏秒钟,就会触发 ANR 错误。

多线程编程才是⼯作中的常态。

多线程是必须掌握的!越早越好!2.⽹络上 delphi 多线程 Demo 的误区采⽤ API 来实现多线程。

难度太⾼,尤其是⽤指针来传参数,不适合初学者。

⽤访问界⾯来举例,出发点就错了。

多线程最不擅长的就是操作UI。

⽹上流⾏的 demo 就是弄个⼤循环,再来⼀个 TextOut 输出。

结果⼜不得不加上 canvas.Lock,真是够折腾。

3.主线程的定义假设,⼀个EXE程序,拥有⼀个 FrmMain (TForm)。

FrmMain 上⾯有⼀个 Button1 (TButton) ,⼀个Edit1 (TEdit)以及⼀个 Timer1 (TTimer) 。

那么,我们通常把界⾯(UI)定义为主线程,即 FrmMain 就是主线程。

Button1 的 OnClick 事件中的代码运⾏于主线程时空。

(本教程均统⼀定义线程时空⼀词)使⽤者在 Edit1 中的输⼊操作也是主线程时空。

Timer1 的 OnTimer 事件中的代码也运⾏于主线程时空。

请注意:初学者最容易把 OnTimer 事件误认为是多线程时空。

4.普通编程与多线程编程的区别// 普通编程function Accumulate(num:integer):integer;vari:integer;beginresult:=0;if num<1then exit;for i:=1to num doresult:=result+i;end;// 在 Button1 的 OnClick 事件中编写如下代码:varn,Total:integer;beginn:=100;Total:=Accumelate(n);// 等待计算结果,假设计算需要5分钟,此处就得等待5分钟。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

Delphi中有一个线程类TThread是用来实现多线程编程的,这个绝大多数Delphi书藉都有说到,但基本上都是对TThread类的几个成员作一简单介绍,再说明一下Execute的实现和Synchronize的用法就完了。

然而这并不是多线程编程的全部,我写此文的目的在于对此作一个补充。

线程本质上是进程中一段并发运行的代码。

一个进程至少有一个线程,即所谓的主线程。

同时还可以有多个子线程。

当一个进程中用到超过一个线程时,就是所谓的“多线程”。

那么这个所谓的“一段代码”是如何定义的呢?其实就是一个函数或过程(对Delphi 而言)。

如果用Windows API来创建线程的话,是通过一个叫做CreateThread的API函数来实现的,它的定义为:HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes,DWORD dwStackSize,LPTHREAD_START_ROUTINE lpStartAddress,LPVOID lpParameter,DWORD dwCreationFlags,LPDWORD lpThreadId);其各参数如它们的名称所说,分别是:线程属性(用于在NT下进行线程的安全属性设置,在9X下无效),堆栈大小,起始地址,参数,创建标志(用于设置线程创建时的状态),线程ID,最后返回线程Handle。

其中的起始地址就是线程函数的入口,直至线程函数结束,线程也就结束了。

整个线程的执行过程如下图所示:因为CreateThread参数很多,而且是Windows的API,所以在C Runtime Library里提供了一个通用的线程函数(理论上可以在任何支持线程的OS中使用):unsigned long _beginthread(void (_USERENTRY *__start)(void *), unsigned__stksize, void *__arg);Delphi也提供了一个相同功能的类似函数:function BeginThread(SecurityAttributes: Pointer; StackSize: LongWord; ThreadFunc: TThreadFunc; Parameter: Pointer; CreationFlags: LongWord; var ThreadId: LongWord): Integer;这三个函数的功能是基本相同的,它们都是将线程函数中的代码放到一个独立的线程中执行。

线程函数与一般函数的最大不同在于,线程函数一启动,这三个线程启动函数就返回了,主线程继续向下执行,而线程函数在一个独立的线程中执行,它要执行多久,什么时候返回,主线程是不管也不知道的。

正常情况下,线程函数返回后,线程就终止了。

但也有其它方式:Windows API:VOID ExitThread( DWORD dwExitCode );C Runtime Library:void _endthread(void);Delphi Runtime Library:procedure EndThread(ExitCode: Integer);为了记录一些必要的线程数据(状态/属性等),OS会为线程创建一个内部Object,如在Windows中那个Handle便是这个内部Object的Handle,所以在线程结束的时候还应该释放这个Object。

虽然说用API或RTL(Runtime Library)已经可以很方便地进行多线程编程了,但是还是需要进行较多的细节处理,为此Delphi在Classes单元中对线程作了一个较好的封装,这就是VCL的线程类:TThread使用这个类也很简单,大多数的Delphi书籍都有说,基本用法是:先从TThread派生一个自己的线程类(因为TThread是一个抽象类,不能生成实例),然后是Override抽象方法:Execute(这就是线程函数,也就是在线程中执行的代码部分),如果需要用到可视VCL对象,还需要通过Synchronize过程进行。

关于之方面的具体细节,这里不再赘述,请参考相关书籍。

本文接下来要讨论的是TThread类是如何对线程进行封装的,也就是深入研究一下TThread类的实现。

因为只是真正地了解了它,才更好地使用它。

下面是DELPHI7中TThread类的声明(本文只讨论在Windows平台下的实现,所以去掉了所有有关Linux平台部分的代码):TThread = classprivateFHandle: THandle;FThreadID: THandle;FCreateSuspended: Boolean;FTerminated: Boolean;FSuspended: Boolean;FFreeOnTerminate: Boolean;FFinished: Boolean;FReturnValue: Integer;FOnTerminate: TNotifyEvent;FSynchronize: TSynchronizeRecord;FFatalException: TObject;procedure CallOnTerminate;class procedure Synchronize(ASyncRec: PSynchronizeRecord); overload;function GetPriority: TThreadPriority;procedure SetPriority(Value: TThreadPriority);procedure SetSuspended(Value: Boolean);protectedprocedure CheckThreadError(ErrCode: Integer); overload;procedure CheckThreadError(Success: Boolean); overload;procedure DoTerminate; virtual;procedure Execute; virtual; abstract;procedure Synchronize(Method: TThreadMethod); overload;property ReturnValue: Integer read FReturnValue write FReturnValue;property Terminated: Boolean read FTerminated;publicconstructor Create(CreateSuspended: Boolean);destructor Destroy; override;procedure AfterConstruction; override;procedure Resume;procedure Suspend;procedure Terminate;function WaitFor: LongWord;class procedure Synchronize(AThread: TThread; AMethod: TThreadMethod); overload;class procedure StaticSynchronize(AThread: TThread; AMethod: TThreadMethod); property FatalException: TObject read FFatalException;property FreeOnTerminate: Boolean read FFreeOnTerminate write FFreeOnTerminate;property Handle: THandle read FHandle;property Priority: TThreadPriority read GetPriority write SetPriority;property Suspended: Boolean read FSuspended write SetSuspended;property ThreadID: THandle read FThreadID;property OnTerminate: TNotifyEvent read FOnTerminate write FOnTerminate;end;TThread类在Delphi的RTL里算是比较简单的类,类成员也不多,类属性都很简单明白,本文将只对几个比较重要的类成员方法和唯一的事件:OnTerminate作详细分析。

首先就是构造函数:constructor TThread.Create(CreateSuspended: Boolean);begininherited Create;AddThread;FSuspended := CreateSuspended;FCreateSuspended := CreateSuspended;FHandle := BeginThread(nil, 0, @ThreadProc, Pointer(Self), CREATE_SUSPENDED, FThreadID);if FHandle = 0 thenraise EThread.CreateResFmt(@SThreadCreateError,[SysErrorMessage(GetLastError)]);end;虽然这个构造函数没有多少代码,但却可以算是最重要的一个成员,因为线程就是在这里被创建的。

在通过Inherited调用TObject.Create后,第一句就是调用一个过程:AddThread,其源码如下:procedure AddThread;beginInterlockedIncrement(ThreadCount);end;同样有一个对应的RemoveThread:procedure RemoveThread;beginInterlockedDecrement(ThreadCount);end;它们的功能很简单,就是通过增减一个全局变量来统计进程中的线程数。

只是这里用于增减变量的并不是常用的Inc/Dec过程,而是用了InterlockedIncrement/InterlockedDecrement这一对过程,它们实现的功能完全一样,都是对变量加一或减一。

但它们有一个最大的区别,那就是InterlockedIncrement/InterlockedDecrement是线程安全的。

即它们在多线程下能保证执行结果正确,而Inc/Dec不能。

相关文档
最新文档