最近一直在溫習舊的知識,剛好學習了一下Java的線程安全方面的知識,今天想起之前一直做的Delphi開發,所以還是有必要溫習一下,看看這些不同的編程語言有什麼不同之處。
Delphi的線程同步方法:
1、臨界區
申明一個臨界資源
FLock : TRTLCriticalSection;
先初化一個臨界資源對象
InitializeCriticalSection(FLock)
銷毀臨界資源對象
DeleteCriticalSection(FLock)
procedure TSaveThread.PushTask(pTask: pFileInfo); begin
//進入臨界資源,有線程占用後,後面的線程會阻塞 EnterCriticalSection(FLock); try
//這裡是受保護的代碼 if not Assigned(FTasks) then FTasks:= TList.Create; FTasks.Add(pTask); finally
//離開臨界資源,其他的阻塞的線程可以進入 LeaveCriticalSection(FLock); end; end;
這種方式還是挺簡單的,和Java中的synchronized代碼塊方式類似
2、Event事件
Event有兩種狀態:有信號和無信號,通過這個特性可以用於線程同步。Event相關的API也多,控制起來也比較靈活,看看他的幾個方法:
CreateEvent:創建事件
SetEvent:設置為有信號狀態
ResetEvent: 設置為無信號狀態
PulseEvent :設置為有信號狀態,接著又設置為無信號狀態(有點類似於單步調試的感覺)
在線程中還是要用WaitForSingleObject來阻塞
procedure TSearchTopNThread.Execute; begin while not Self.Terminated do begin WaitForSingleObject(FEvent, INFINITE);//FEvent為無信號時阻塞,直到變為有信號時代碼繼續執行 ....... end; end;
//設置事件狀態為無信號,阻塞線程 procedure TForm1.Button1Click(Sender: TObject); begin ResetEvent(hEvent); end; //設置事件狀態為有信號,線程解除阻塞 procedure TForm1.Button2Click(Sender: TObject); begin SetEvent(hEvent); end; //設置一次脈沖信號狀態,先設置有信號,再設置無信號 procedure TForm1.Button3Click(Sender: TObject); begin PulseEvent(hEvent); end;
3、互斥對象
var hMutex: THandle; //互斥對象的句柄 function ThreadFun(p: Pointer): DWORD; stdcall; var i: Integer; beginfor i := 0 to 1000 do begin
//使用WaitForSingleObject訪問互斥對象,如果hMutex為被鎖定>=1,那就等待,如果為未鎖定那麼就給hMutex的上鎖,然後運行下面的代碼 if WaitForSingleObject(hMutex, INFINITE) = WAIT_OBJECT_0 then begin //保護中的代碼
.....
//代碼結束後釋放信號量,表示當前線程已經完成處理,後面的線程就可以獲得控制權 ReleaseMutex(hMutex); end; end; Result := 0; end; procedure TForm1.FormCreate(Sender: TObject); begin hMutex := CreateMutex(nil, False, nil);//第二個參數為False表示創建的互斥對象不被任何線程擁有,也就說初始狀態為未鎖定,線程可以占有 end; procedure TForm1.FormDestroy(Sender: TObject); begin CloseHandle(hMutex); end; end.
總的過程和臨界區有些類似,只不過互斥對象是系統核心對象,可以跨進程,而臨界區只是當前進程。有這個特性可以用於對文件等資源的並發保護。
4、信號量
信號量與互斥對象的方法類似,信號量的特點是可以設置計數,就是同時可以允許多個線程同時訪問同一段代碼。互斥對象就是信號量為1的情況,後面的線程只能等前一個執行完,而信號量就可以允許指定多個執行。
後來在看TThread的代碼時發現新的Delphi版本中有了一個好東西:TMnitor
try TMonitor.Enter(ThreadLock); try SyncProcPtr.Queued := QueueEvent; if SyncList = nil then SyncList := TList.Create; SyncProcPtr.SyncRec := ASyncRec; SyncList.Add(SyncProcPtr); SignalSyncEvent; if Assigned(WakeMainThread) then WakeMainThread(SyncProcPtr.SyncRec.FThread); if not QueueEvent then TMonitor.Wait(SyncProcPtr.Signal, ThreadLock, INFINITE) finally TMonitor.Exit(ThreadLock); end;
這個代碼和java中的Synchronized代碼塊很像,這樣寫起線程同步要簡化不少。