精华区 [关闭][返回]

当前位置:网易精华区>>讨论区精华>>编程开发>>● Delphi>>其他>>线程>>扩展Delphi的线程同步对象

主题:扩展Delphi的线程同步对象
发信人: cn_prince(欢喜王子)
整理人: teleme(2001-04-14 19:11:49), 站内信件
在编写多线程应用程序时,最重要的是控制好线程间的同步资源访问,以保证线程的安全运行。Win 32 API提供了一组同步对象,如:信号灯(Semaphore)、互斥(Mutex)、临界区(CriticalSection)和事件(Event)等,用来解决这个问题。 

  Delphi分别将事件对象和临界区对象封装为Tevent对象和TcritialSection对象,使得这两个对象的使用简单且方便。但是如果在Delphi程序中要使用信号灯或互斥等对象就必须借助于复杂的Win32 API函数,这对那些不熟悉Win32 API函数的编程人员来说很不方便。因此,笔者用Delphi构造了两个类,对信号灯和互斥对象进行了封装(分别为TSemaphore和TMutex),希望对广大Delphi编程人员有所帮助。 

  一、类的构造 
  我们先对Win32 API的信号灯对象和互斥对象进行抽象,构造一个父类THandleObjectEx,然后由这个父类派生出两个子类Tsemphore和Tmutex。 

  类的源代码如下: 

  unit SyncobjsEx; 

  interface 

  uses Windows,Messages,SysUtils,Classes,Syncobjs; 

  type 

   THandleObjectEx = class(THandleObject) 

  // THandleObjectEx为互斥类和信号灯类的父类 

   protected 

   FHandle: THandle; 

   FLastError: Integer; 

   public 

   destructor Destroy; override; 

   procedure Release;override; 

   function WaitFor(Timeout: DWORD): TWaitResult; 

   property LastError:Integer read FLastError; 

   property Handle: THandle read FHandle; 

   end; 

   TMutex = class(THandleObjectEx)//互斥类 

   public 

   constructor Create(MutexAttributes: PSecurityAttributes; InitialOwner: Boolean;const Name:string); 

   procedure Release; override; 

   end; 

   TSemaphore = class(THandleObjectEx) 

  //信号灯类 

  public 

  constructor Create(SemaphoreAttributes: PSecurityAttributes;InitialCount:Integer;MaximumCount: integer; const Name: string); 

  procedure Release(ReleaseCount: Integer=1;PreviousCount:Pointer=nil);overload; 

   end; 

  implementation 

  { THandleObjectEx }//父类的实现 

  destructor THandleObjectEx.Destroy; 

  begin 

   Windows.CloseHandle(FHandle); 

   inherited Destroy; 

  end; 

  procedure THandleObjectEx.Release; 

  begin 

  end; 

  function THandleObjectEx.WaitFor(Timeout: DWORD): TWaitResult; 

  //等待函数,参数为等待时间 

  begin 

  case WaitForSingleObject(Handle, Timeout) of 

  WAIT_ABANDONED: Result := wrAbandoned; 

  //无信号 

  WAIT_OBJECT_0: Result := wrSignaled; 

  //有信号 

  WAIT_TIMEOUT: Result := wrTimeout;//超时 

  WAIT_FAILED://失败 

   begin 

   Result := wrError; 

   FLastError := GetLastError; 

   end; 

   else 

   Result := wrError; 

   end; 

  end; 

  { TSemaphore }//信号灯类的实现 

  constructor TSemaphore.Create(SemaphoreAttributes: PSecurityAttributes; 

   InitialCount, MaximumCount: integer; const Name: string);//信号灯类的构造函数 

  begin 

  FHandle := CreateSemaphore 

  (SemaphoreAttributes,InitialCount, 

  MaximumCount,PChar(Name)); 

  //四个参数分别为:安全属性、初始信号灯计数、最大信号灯计数、信号灯名字 

  end; 

  procedure TSemaphore.Release(ReleaseCount: Integer=1; PreviousCount: Pointer=nil); 

  //信号灯类的Release方法,每执行一次按指定量增加信号灯计数 

  begin 

   Windows.ReleaseSemaphore(FHandle, ReleaseCount, PreviousCount); 

  end; 

  { TMutex }//互斥类的实现 

  constructor TMutex.Create(MutexAttributes: PSecurityAttributes; 

  InitialOwner: Boolean; const Name: string); 

  //互斥类的构造函数 

  begin 

   FHandle := CreateMutex(MutexAttributes, InitialOwner, PChar(Name)); 

  end; 

  procedure TMutex.Release;//互斥类的Release方法,用来释放对互斥对象的所有权 

  begin 

   Windows.ReleaseMutex(FHandle); 

  end; 

  end. 

  二、信号灯对象与互斥对象的使用 
  1. 信号灯对象 

  信号灯对象维持一个从0到指定最大值之间的数。在其计数大于0时是有信号的,而在其计数为0时是无信号的。信号灯对象可用来限制对共享资源进行访问的线程数量,例如应用程序可使用信号灯对象来限制它建立的窗口数量。 

  用类的Create方法来建立信号灯对象,在调用该方法时,可以指定对象的初始计数和最大计数。该方法有四个参数,依次为:安全属性、初始计数、最大计数和对象名字(以便别的进程的线程可打开指定名字的信号灯句柄)。如: 

  Semaphore := TSemaphore.Create(nil,10,10,''); 

  一般把信号灯的初始计数设置成最大值。每次当信号灯有信号并等待函数返回时,信号灯计数就会减1,而通过调用对象的Release方法可按指定量增加信号灯的计数(默认为加1)。计数值越小就表明访问共享资源的程序越多。如:“Semaphore.Release(3, nil);”,其中第一个参数为增加的信号灯数量,第二个参数为执行该方法之前的信号灯数量。信号灯用法举例: 

  if wrSignaled = Semaphore.WaitFor(10000) then//若信号灯是有信号的 

  begin 

   //打开另一个窗口 

  end 

   Semaphore.Release() 

  在线程建立窗口之前,它使用WaitFor函数确定信号灯的当前计数是否允许建立新的窗口,等待时间设为10秒。 

  2. 互斥对象 

  Mutex对象的状态在它不被任何线程拥有时是有信号的,而当它被拥有时则是无信号的。Mutex对象很适合用来协调多个线程对共享资源的互斥访问(mutually exclusive)。例如,有几个线程共享对数据库的访问时,线程可以使用Mutex对象,一次只允许一个线程向数据库写入。 

  用类的Create方法建立Mutex 对象,在建立Mutex 时,可以为对象起个名字,这样其他进程中的线程可以打开指定名字的Mutex对象句柄。例如: 

  Mutex := TMutex.Create(nil, False, ''); 

  在完成对共享资源的访问后,可以调用Release方法来释放Mutex,以便让别的线程能访问共享资源。如果线程终止而不释放Mutex,则认为该Mutex被废弃。 

  互斥对象用法举例如下: 

  if wrSignaled = Mutex.WaitFor(10000) then//若获得互斥对象的拥有权 

   begin 

   try 

   //往数据库写入 

   finally 

  Mutex.Release;//释放对互斥对象的拥有权 

   end; 

   end; 

  上述类代码在Windows 98,Delphi 5.0下调试通过。



----
prince 

[关闭][返回]