Delphi5學習筆記之四

uuxa發表於2007-04-03

142.執行緒同步問題:可以使用臨界區、互斥量、訊號量和事件來實現執行緒同步。

臨界區:它就是一次只能由一個執行緒執行的一段程式碼。在使用臨界區的方法:

1步:呼叫過程procedure InitializeCriticalSection(

var lpCriticalSection: TRTLCriticalSection);初始化臨界區。

2步:呼叫過程procedure EnterCriticalSection(

var lpCriticalSection: TRTLCriticalSection);進入臨界區。

3步:執行臨界區中的程式碼

4步:呼叫過程LeaveCritcalSection(var lpCriticalSection: TRTLCriticalSection);退出臨界區。

5步:呼叫過程DeleteCriticalSection(var lpCriticalSection: TRTLCriticalSection);刪除臨界區。

舉例說明:

var

CS: TRTLCriticalSection;

Procedure SomeThread.Execute;

Var

I: integer;

Begin

EnterCriticalSection(CS);

For I := 0 to 900000 do begin

GI := Round(abs(sqrt(I)));

End;

LeaveCriticalSection(CS);

End;

Initicalization

IniticalizeCriticalSection(CS);//一般情況,初始化臨界區都是與執行緒建立放到一起

Finallization

DeleteCriticalSection(CS);//在使用臨界區的所有執行緒都終止後刪除臨界區

End.//初始化臨界區和刪除臨界區不一定放到程式的初始化和終止區。

[@more@]

互斥量:它與臨界區的區別:互斥量可以跨程式的執行緒同步,還有一個更大的區別就是效能方面,臨界區要比事件物件(如:互斥物件)快很多,這是因為事件物件使用了系統核心。

函式CreateMutex用來建立一個互斥量,

function CreateMutex(lpMutexAttributes: PsecurityAttributes;

bInitialOwner: Bool;lpName: PChar):THandle;

引數說明:

lpMutexAttributes是一個指向PsecurityAttributes結構的指標。此引數一般設定為0,表示使用預設的安全屬性。

BInitialOwner表示建立這個互斥物件的執行緒是否要成為這個互斥物件的擁有者。

LpName是此互斥物件的名稱。

注:當使用完互斥物件時,應當使用CloseHandle來關閉它。

使用互斥量物件的步驟:

在建立完互斥量物件後要進行如下幾步來使用它:

第一步:使用WaitForSingleObject來防止其它執行緒進入互斥量保護的程式碼。

第二步:執行你要在互斥量下保護的程式碼

第三步:使用ReleaseMutex來釋放互斥量物件。

第四步:使用CloseHandle來關閉互斥量物件。

舉例說明:

const MaxSize = 200;

var

GI: Integer = 0;

hMutex: THandle = 0;

ThreadDone: Byte = 0;//用於記錄終止的執行緒個數

GArray: array [1..MaxSize] of Integer;//全域性陣列GArray

hMutex := CreateMutex(nil,False,nil);//傳遞False表示建立互斥量的執行緒不擁有該互斥量

//建立兩個執行緒

TCustomThread.Create(False);

TCustomThread.Create(False);

procedure TCustomThread.Execute;

var

I: integer;

begin

FreeOnTerminate := True;(*設定FreeOnTerminate,

在執行完Execute後就會觸發OnTerminate事件,

這樣就有機會在OnTerminate事件裡清除執行緒物件了

*)

OnTerminate := MainForm.CustomeThreadOnTerminate;

if WaitForSingleObject(hMutex,INFINITE) = WAIT_OBJECT_0 then begin

(*當第一次執行WaitForSingleObject,由於建立的互斥物件不屬於任何執行緒,因此

hMutex標識的互斥物件是有訊號的,因此本條件為真,可以執行下面的程式碼,當執行完

WaitForSingleObject,WaitForSingleObject會將hMutex標識的互斥物件變成無訊號狀態,這樣,再有其它執行緒執行到WaitForSingleObject,它的返回值就不是WAIT_OBJECT_0,

這樣就不能執行下面的程式碼. :WAIT_OBJECT_0表示互斥物件有訊號.

*)

if not Terminated then begin

for i := 1 to MaxSize do begin

GArray[i] := GetGI;//GetGI是一個給陣列Garray初始化的函式

end;

end;

end;

ReleaseMutex(hMutex);

//釋放互斥量物件,使互斥量物件變成無訊號狀態,這樣其它的執行緒再次呼叫WaitForSingleObject函式時,就可以得知目前該互斥物件所保護的程式碼是可以訪問的,即WaitForSingleObject會返回WAIT_OBJECT_0。不過我在實現上述程式碼時,發現,全域性陣列GArray在第一個執行緒終止後輸出時,由於第二個執行緒此時可以訪問互斥物件保護的程式碼,也就是可以訪問全域性陣列GArray,即改寫全域性陣列GArray,而此時第一個執行緒終止時GArray的輸出還未結束,這就造成資料輸出邏輯錯誤。

end;

procedure TMainForm.CustomeThreadOnTerminate(Sender: TObject);//執行緒終止時需執行的過程CustomThreadOnTerminate

var

I: integer;

begin

Inc(ThreadDone);

if ThreadDone = 1 then begin//在第一個執行緒終止後輸出全域性陣列裡的資料時會發生邏輯錯誤目前還未解決

for I := 1 to MaxSize do begin

MemoThread.Lines.Add(IntToStr(GArray[i]));

end;

end;

if ThreadDone = 2 then begin CloseHandle(hMutex); end;

end;

訊號量:首先說明幾點,一、訊號量不屬於任何執行緒,二、訊號量一般用於對資源的控制。

使用函式CreateSemaphore來建立訊號量,

function CreateSemaphore(lpSemaphoreAttributes: PsecurityAttributes;

lInitialCount,lMaximumCount: LongInt;lpName: PChar):THandle;

引數說明:

lInitialCount是訊號量的計數值,它必須在0lMaximumCount之間,如果lInitialCount大於零,說明訊號量處於有訊號狀態,當呼叫WaitForSingleObject等函式時lInitialCount就減少1。當呼叫函式ReleaseSemaphorelInitialCount就加1

lMaximumCount是計數的最大值,如果訊號量代表的是資源,那麼lMaximumCount就代表資源的總數。

lpName是訊號量的名稱。

使用訊號量的方法與使用互斥量的方法及步驟相同。不過釋放訊號量的函式ReleaseSemaphore要複雜一些,

function ReleaseSemaphore(hSemaphore: THandle;lReleaseCount: Longint;

lpPreviousCount: Pointer): BOOL;

引數說明:

lReleaseCount表示在使用該函式時給訊號量計數增加的值,如果說把它賦值為9那麼該函式就會將訊號量的計數增加9,而互斥量釋放函式一次只能釋放一個。

LpPreviousCount如果該引數的值不為nil,那麼它將用來儲存原有訊號量的計數值。

注在使用完訊號量後也要使用CloseHandle來關閉訊號量的控制程式碼。

143.使用任何執行緒物件的方法來操作使用者介面時,一定要透過Synchronize()方法來呼叫,或者透過訊息來控制使用者介面。

144.圖形單元Graphic.pas是執行緒安全的,包括TCanvasTPenTBrushTFontTBitmapTMetafileTPictureTIcon類。之所以說它們是安全的,是因為這些類中使用了方法LockUnLock。這兩個方法類似臨界區的功能。如下所示:

Canvas.Lock;

Canvas.TextOut(x,y,’’);//LockUnLock間夾著的部分Canvas.TextOut被保護

Canvas.UnLock;

如上所示,夾在Canvas.LockCanvas.UnLock方法間的對Canvas的操作就被保護了起來,當一個執行緒執行中間的部分(Canvas.TextOut(x,y,’’))時,如果其它執行緒也要執行這段程式碼是不可以的,只有噹噹前執行緒執行完中間部分後,其它執行緒才可以執行。

145.複習記錄中的Packed,為了使訪問方便,向陣列和記錄這樣的結構化型別在記憶體中都是按照字或雙字排列的,這樣會佔用掉多於實際需要的空間。不過在記錄宣告時加上保留字packed時,就可以資料儲存是緊湊儲存的。

146. 可以透過將文字檔案和二進位制檔案變數轉換為TTextRecTFileRec來獲得檔案的控制程式碼,如:TfileRec(SomeFileVar).Handle;

147. 對映檔案的三種典型應用如下:

Win32系統中,利用記憶體對映檔案實現調入和執行EXE檔案及DLL檔案。這樣節省了頁交換檔案的空間,從而減少了檔案調入的時間。

僅僅利用一個指向記憶體區域的指標就可實現對對映檔案的資料訪問,這樣不僅簡化了對檔案的訪問,也不需要編寫各種緩衝方案。

在同一臺機器上,利用記憶體對映檔案可以實現多個程式共享資料。

148. 當建立記憶體對映檔案時,實際上是使檔案與程式虛擬空間的一段區域關聯。為建立關聯,首先需要建立一個檔案對映物件。要檢視或編輯檔案的內容,必須獲得檔案對映物件的檢視。這樣,當透過指標訪問檔案的內容時,就如同訪問記憶體區域一樣了。

149. 當向檔案檢視寫入資料時,系統負責處理資料的快取、緩衝、寫入和調入以及記憶體的分配和釋放。這好像是在一塊記憶體區域中編輯資料。檔案的輸入/輸出完全由系統來處理。這就是記憶體對映檔案的美妙之處。

150. 使用對映檔案

1.建立/開啟一個記憶體對映檔案的第一步是獲取一個檔案的控制程式碼。你可以呼叫FileCreate()FileOpen()FileCreate()SysUtils.pas單元中宣告,此函式建立一個檔名為FileName的新檔案。如果函式呼叫成功則返回一個有效的檔案控制程式碼。否則,返回INVA LID_HANDLE_VALUE常量。

FileOpen()用來以某種模式開啟一個已存在的檔案。如果函式呼叫成功則返回一個有效的檔案控制程式碼。否則,返回INVALID_HANDLE_VALUE的常量。FileOpen()SysUtils.pas單元中宣告

2建立檔案對映物件,無論是命名的或是無命名的記憶體對映檔案物件,你都可以使用CreateFileMapping()函式來建立。如果CreateFileMapping()呼叫成功,則返回檔案對映物件的控制程式碼。如果此控制程式碼代表一個已有的檔案對映物件,GetLastError()將返回ERROR_ALREADY_EXISTS。如果CreateFileMapping()呼叫失敗,將返回0。此時,必須呼叫GetLastError()得到失敗的原因。

3對映檔案的檢視到程式的地址空間,MapViewOfFile()能把檔案的檢視對映到程式地址空間。

4取消檔案檢視的對映,UnmapViewOfFile()用來解除檔案檢視與程式地址空間的對映關係。完成對檔案的處理後,必須呼叫UnmapViewOfFile();否則,只能等到程式終止,系統才會釋放對映區域所佔的記憶體。

5關閉檔案對映和檔案核心物件,對FileOpen()CreateFileMapping()的呼叫都將開啟核心物件,你必須負責關閉這些核心物件。這可以透過呼叫CloseHandle()函式來實現。

151.使用函式GetDriveType可以得到驅動器的一些資訊,它有一個Pchar型別的引數,它的返回值為整數,表示驅動器的型別,如:

var

I: integer;

C: String;

Dtype: Integer;

DriveString: String;

Begin

For I := 65 to 90 do

Begin

C := Chr(i) + ‘:’;

Dtype := GetDrvieType(Pchar(C));

Case DType of

0://不能確定驅動器型別

1: //根目錄不存在

DRIVE_REMOVABLE: //驅動器可移動

DRIVE_FIXED: //驅動器不可移動

DRIVE-REMOTE: //驅動器為遠端網路驅動器

DRIVE_CDROM: //驅動器為CD-ROM驅動器

DRIVE_RAMDISK: //驅動器為RAM磁碟

End;

End;

End;

152.GetDiskFreeSpace可以得到驅動器的以下資訊:每一簇的扇區數、 每一扇區的位元組數、 可用簇數、簇的總數、可用的磁碟位元組數、總的磁碟位元組數。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/7416120/viewspace-908612/,如需轉載,請註明出處,否則將追究法律責任。

相關文章