在編寫多線程應用程序時,最重要的是控制好線程間的同步資源訪問,以保證線程的安全運行。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;