2010-02-22 17:08:46
Delphi把多线程相关的API封装在TThread这个类中,可以方便实现多线程运用。首先看下TThread的声明:
TThread = class
private
FHandle: 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);
protected
procedure CheckThreadError(ErrCode: Integer); overload;
procedure CheckThreadError(Success: Boolean); overload;
procedure DoTerminate; virtual;
procedure Execute; virtual; abstract;
procedure Synchronize(Method: TThreadMethod); overload;
property ReturnValu ......
在使用多线程的时候,如果多线程对某个特定的公共数据或资源进行访问,需要对多线程进行协调操作,叫做线程同步。
例如:三个线程分别循环地向ListBox中写入数据。没有进行同步时,写入的顺序是不确定的。
{主窗体代码}
unit Unit2;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm2 = class(TForm)
ListBox1: TListBox;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form2: TForm2;
implementation
uses MyThread;
{$R *.dfm}
procedure TForm2.Button1Click(Sender: TObject);
begin
TMyThread.Create(False);
TMyThread.Create(False);
TMyThread.Create(False);
end;
end.{多线程代码}
unit MyThread;
interface
uses
Classes,StdCtrls,SysUtils,Windows;
type
TMyThread = class(TThread)
private
{ Private declarations }
s ......
临界区是一段代码,一次只允许一个线程执行这段代码。当把一段代码放入一个临界区, 线程执行到临界区时就独占了, 其他线程如果要访问这段代码,一定要等前一个访问的线程结束才行。借用前面的比喻比作图书馆,临界区就像把图书馆设计成只有一个位置(现实中好像不太合理,不管了,反正这么个意思),当有一个人正在办理业务时,其他人只能等待那个人办完业务才能进去。
使用临界区的步骤:
1、声明一个TRLCriticalSection记录类型的变量如CS,必须是全局的;
2、使用前先初始化:InitializeCriticalSection(CS);
3、EnterCriticalSection(CS); 线程进入临界区,其他线程需要等待
4、LeaveCriticalSection(CS); 线程离开临界区,其他线程可以访问了
5、DeleteCriticalSection(CS); 最后删除临界区
重写前文的例子,如下:
{主窗体代码}
unit Unit2;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm2 = class(TForm)
L ......
互斥量是系统内核对象,谁拥有就谁执行。它与临界区工作很类似。不同处在于:1、互斥量可以跨进程边界同步线程。2、可以给互斥量取个名字,通过引用互斥量的名字来使用一个已知的互斥量对象。
使用互斥量之类的对象需要反复调用系统内核,期间需要进行进程上下文转换和控制级别转换,大概需要耗费400到600个时间周期。
又是图书馆的比喻,现在是搞一个锁,把钥匙(互斥量句柄)交给管理员(操作系统),每一个人(线程)想要借书的时候,都要向管理员拿钥匙。当有人在使用的时候,另一人必须等待,等到钥匙有空的时候(互斥量进入信号状态),才能拿到钥匙(拥有了句柄)办理借书业务(此时互斥量进入非信号状态直到办完业务)。
使用互斥量的步骤:
1、声明一个全局的互斥量句柄变量(var hMutex: THandle;);
2、创建互斥量:CreateMutex(
lpMutexAttributes: PSecurityAttributes;
  ......
信号量是建立在互斥量的基础之上,同时加入重要特性:提供了资源计数功能,因此预定义数量的线程同时可以进入同步的代码块中。
信号量是维护0到指定最大值之间的计数器的同步对象,当线程完成一次信号量的等待时,计数器自减1,当线程释放信号量对象时,计数器自增1。
借用上面的图书馆例子,信号量好像是多设几把管理钥匙。每次可以设定N把钥匙同时工作,那就有N个人员可以同时办理业务。
信号量使用的一般步骤:
1、声明一个全局的信号量名柄,如:hSem:THandle;
2、创建信号量:CreateSemphore(
lpSemaphoreAttributes:PSecurityAttributes;
lInitialCount,lMaximumCount:LongInt;
......
Event事件用法与Mutex差不多,但它可以使用:SetEvent(启动运行)ResetEvent(暂停运行)、PulseEvent(执行一次后立即暂停)。
先看一下创建函数:CreateEvent(
lpEventAttributes: PSecurityAttributes;
bManualReset: BOOL;
bInitialState: BOOL;
&nbs ......