Delphi 中多線程同步的一些處理方法

當創建了多個線程,並且多個線程都要訪問同一資源,,就有可能出現混亂,於是用Synchronize來控制,使同一時間只有一個線程使用那部分資源,Synchronize參數裏面的代碼就是多線程需要公共的代碼!


線程是進程內一個相對獨立的、可調度的執行單元。一個應用可以有一個主線程,一個主線程可以有多個子線程,子線程還可以有自己的子線程,這樣就構成了多線程應用了。由於多個線程往往會同時訪問同一塊內存區域,頻繁的訪問這塊區域,將會增加產生線程衝突的概率。一旦產生了衝突,將會造成不可預料的結果(該公用區域的值是不可預料的)可見處理線程同步的必要性。
   注意:本文中出現的所有代碼都是用Delphi描述的,調試環境爲Windows me ,Delphi 6。其中所涉及的Windows API函數可以從MSDN獲得詳細的文檔。
   首先引用一個實例來引出我們以下的討論,該實例沒有采取任何措施來避免線程衝突,它的主要過程爲:由主線程啓動兩個線程對letters這個全局變量進行頻繁的讀寫,然後分別把修改的結果顯示到ListBox中。由於沒有同步這兩個線程,使得線程在修改letters時產生了不可預料的結果。
   ListBox中的每一行的字母都應該一致,但是上圖畫線處則不同,這就是線程衝突產生的結果。當兩個線程同時訪問該共享內存時,一個線程還未對該內存修改完,另一個線程又對該內存進行了修改,由於寫值的過程沒有被串行化,這樣就產生了無效的結果。可見線程同步的重要性。 

   以下是本例的代碼


   unit.pas文件
   unit Unit1;
   interface
   uses
   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
   Dialogs, StdCtrls;
   
   //定義窗口類
   type
   TForm1 = class(TForm)
   ListBox1: TListBox;
   ListBox2: TListBox;
   Button1: TButton;
   procedure Button1Click(Sender: TObject);
   private
   { Private declarations }
   public
   { Public declarations }
   end;
   
   //定義線程類
   type
   TListThread=class(TThread)
   private
   Str:String;
   protected
   procedure AddToList;//將Str加入ListBox組件
   Procedure Execute;override;
   public
   LBox:TListBox;
   end;
   //定義變量
   var
   Form1: TForm1;
   Letters:String='AAAAAAAAAAAAAAAAAAAA';//全局變量
   
   implementation
   
   {$R *.dfm}
   
   //線程類實現部分
   procedure TListThread.Execute;
   var
   I,J,K:Integer;
   begin
   for i:=0 to 50 do
   begin
   for J:=1 to 20 do
   for K:=1 to 1000 do//循環1000次增加產生衝突的機率
   if letters[j]<'Z' then
   letters[j]:=succ(Letters[j])
   else
   letters[j]:='A';
   str:=letters;
   synchronize(addtolist);//同步訪問VCL可視組件
   end;
   end;
   
   procedure TListThread.AddToList;
   begin
   LBox.Items.Add(str);//將str加入列表框
   end;
   
   //窗口類實現部分
   procedure TForm1.Button1Click(Sender: TObject);
   var
   th1,th2:TListThread;
   begin
   Listbox1.Clear;
   Listbox2.Clear;
   th1:=tlistThread.Create(true);//創建線程1
   th2:=tlistThread.Create(true);//創建線程2
   th1.LBox:=listBox1;
   th2.LBox:=listBox2;
   th1.Resume;//開始執行
   th2.Resume;
   end;
   end.
   
   由上例可見,當多個線程同時修改一個公用變量時,會產生衝突,所以我們要設法防止它,這樣我們開發的多線程應用才能夠穩定地運行。下面我們來改進它。我們先使用臨界段來串行化,實現同步。在上例unit1.pas代碼的uses段中加入SyncObjs單元,加入全局臨界段變量(TRTLCriticalSection)Critical1,在FormCreate事件中加入InitializeCriticalSection(Critical1)這句代碼,在FormDestroy事件中加入DeleteCriticalSection(Critical1)這句代碼,然後修改TListThread.Execute函數,修改後的代碼似如下所示(?處爲增加的代碼):
   procedure TListThread.Execute;
   var
   I,J,K:Integer;
   begin
   for i:=0 to 50 do
   begin
   ?EnterCriticalSection(Critical1);//進入臨界段
   for J:=1 to 20 do
   for K:=1 to 3000 do
   if letters[j]<'Z' then
   letters[j]:=succ(Letters[j])
   else
   letters[j]:='A';
   str:=letters;
   ?LeaveCriticalSection(Critical1);//退出臨界段
   synchronize(addtolist);
   end;
   end;
   好了,重新編譯,運行結果如下圖所示(略)
   
   程序成功的避免了衝突,看來真的很簡單,我們成功了!當然我們還可以使用其它同步技術如Mutex(互斥對象), Semaphore(信號量)等,這些技術都是Windows通過API直接提供給我們的。


   
   下面總結一下Windows常用的幾種線程同步技術。


   1. Critical Sections(臨界段),源代碼中如果有不能由兩個或兩個以上線程同時執行的部分,可以用臨界段來使這部分的代碼執行串行化。它只能在一個獨立的進程或一個獨立的應用程序中使用。使用方法如下:
   //在窗體創建中
   InitializeCriticalSection(Critical1)
   //在窗體銷燬中
   DeleteCriticalSection(Critical1)
   //在線程中
   EnterCriticalSection(Critical1)
   ……保護的代碼
   LeaveCriticalSection(Critical1)
   2. Mutex(互斥對象),是用於串行化訪問資源的全局對象。我們首先設置互斥對象,然後訪問資源,最後釋放互斥對象。在設置互斥對象時,如果另一個線程(或進程)試圖設置相同的互斥對象,該線程將會停下來,直到前一個線程(或進程)釋放該互斥對象爲止。注意它可以由不同應用程序共享。使用方法如下:
   //在窗體創建中
   hMutex:=CreateMutex(nil,false,nil)
   //在窗體銷燬中
   CloseHandle(hMutex)
   //在線程中
   WaitForSingleObject(hMutex,INFINITE)
   ……保護的代碼
   ReleaseMutex(hMutex)
   3. Semaphore(信號量),它與互斥對象相似,但它可以計數。例如可以允許一個給定資源同時同時被三個線程訪問。其實Mutex就是最大計數爲一的Semaphore。使用方法如下:
   //在窗體創建中
   hSemaphore:= CreateSemaphore(nil,lInitialCount,lMaximumCount,lpName)
   //在窗體銷燬中
   CloseHandle(hSemaphore)
   //在線程中
   WaitForSingleObject(hSemaphore,INFINITE)
   ……保護的代碼
   ReleaseSemaphore(hSemaphore, lReleaseCount, lpPreviousCount)
   4. 還可以使用Delphi中的TcriticalSection這個VCL對象,它的定義在Syncobjs.pas中。
   
   當你開發多線程應用時,並且多個線程同時訪問一個共享資源或數據時,你需要考慮線程同步的問題了。
   
   delphi中多線程同步的一些方法
   [ 2006-01-09 10:48:03 | 作者: snox 字體大小:大 |中 |小 ]
   當有多個線程的時候,經常需要去同步這些線程以訪問同一個數據或資源。例如,假設有一個程序,其中一個線程用於把文件讀到內存,而另一個線程用於統計文件中的字符數。當然,在把整個文件調入內存之前,統計它的計數是沒有意義的。但是,由於每個操作都有自己的線程,操作系統會把兩個線程當作是互不相干的任務分別執行,這樣就可能在沒有把整個文件裝入內存時統計字數。爲解決此問題,你必須使兩個線程同步工作。 
   存在一些線程同步地址的問題,Win32提供了許多線程同步的方式。在本節你將看到使用臨界區、 互斥、信號量和事件來解決線程同步的問題。


   1. 臨界區 
   臨界區是一種最直接的線程同步方式。所謂臨界區,就是一次只能由一個線程來執行的一段代碼。如果把初始化數組的代碼放在臨界區內,另一個線程在第一個線程處理完之前是不會被執行的。
   在使用臨界區之前,必須使用InitializeCriticalSection()過程來初始化它。
   其聲明如下:
   procedure InitializeCriticalSection(var
   lpCriticalSection參數是一個TRTLCriticalSection類型的記錄,並且是變參。至於TRTLCriticalSection 是如何定義的,這並不重要,因爲很少需要查看這個記錄中的具體內容。只需要在lpCriticalSection中傳遞未初始化的記錄,InitializeCriticalSection()過程就會填充這個記錄。 
   注意Microsoft故意隱瞞了TRTLCriticalSection的細節。因爲,其內容在不同的硬件平臺上是不同的。在基於Intel的平臺上,TRTLCriticalSection包含一個計數器、一個指示當前線程句柄的域和一個系統事件的句柄。在Alpha平臺上,計數器被替換爲一種Alpha-CPU 數據結構,稱爲spinlock。在記錄被填充後,我們就可以開始創建臨界區了。這時我們需要用EnterCriticalSection()和LeaveCriticalSection()來封裝代碼塊。這兩個過程的聲明如下:
   procedure EnterCriticalSection(var lpCriticalSection:TRRLCriticalSection);stdcall;
   procedure LeaveCriticalSection(var
   正如你所想的,參數lpCriticalSection就是由InitializeCriticalSection()填充的記錄。 
   當你不需要TRTLCriticalSection記錄時,應當調用DeleteCriticalSection()過程,下面是它的聲明:
   procedure DeleteCriticalSection(var
   
   2. 互斥 
   互斥非常類似於臨界區,除了兩個關鍵的區別:首先,互斥可用於跨進程的線程同步。其次,互斥能被賦予一個字符串名字,並且通過引用此名字創建現有互斥對象的附加句柄。
   提示臨界區與事件對象(比如互斥對象)的最大的區別是在性能上。臨界區在沒有線程衝突時,要用1 0 ~ 1 5個時間片,而事件對象由於涉及到系統內核要用400~600個時間片。
   可以調用函數CreateMutex ( )來創建一個互斥量。下面是函數的聲明:
   function
   lpMutexAttributes參數爲一個指向TSecurityAttributtes記錄的指針。此參數通常設爲0,表示默認的安全屬性。bInitalOwner參數表示創建互斥對象的線程是否要成爲此互斥對象的擁有者。當此參數爲False時, 表示互斥對象沒有擁有者。
   lpName參數指定互斥對象的名稱。設爲nil表示無命名,如果參數不是設爲nil,函數會搜索是否有同名的互斥對象存在。如果有,函數就會返回同名互斥對象的句柄。否則,就新創建一個互斥對象並返回其句柄。
   當使用完互斥對象時,應當調用CloseHandle()來關閉它。
   在程序中使用WaitForSingleObject()來防止其他線程進入同步區域的代碼。此函數聲明如下: 
   function
   
   這個函數可以使當前線程在dwMilliseconds指定的時間內睡眠,直到hHandle參數指定的對象進入發信號狀態爲止。一個互斥對象不再被線程擁有時,它就進入發信號狀態。當一個進程要終止時,它就進入發信號狀態。dwMilliseconds參數可以設爲0,這意味着只檢查hHandle參數指定的對象是否處於發信號狀態,而後立即返回。dwMilliseconds參數設爲INFINITE,表示如果信號不出現將一直等下去。 
   這個函數的返回值如下
   WaitFor SingleObject()函數使用的返回值
   返回值 含義
   WAIT_ABANDONED 指定的對象是互斥對象,並且擁有這個互斥對象的線程在沒有釋放此對象之前就已終止。此時就稱互斥對象被拋棄。這種情況下,這個互斥對象歸當前線程所有,並把它設爲非發信號狀態
   WAIT_OBJECT_0 指定的對象處於發信號狀態
   WAIT_TIMEOUT等待的時間已過,對象仍然是非發信號狀態再次聲明,當一個互斥對象不再被一個線程所擁有,它就處於發信號狀態。此時首先調用WaitForSingleObject()函數的線程就成爲該互斥對象的擁有者,此互斥對象設爲不發信號狀態。當線程調用ReleaseMutex()函數並傳遞一個互斥對象的句柄作爲參數時,這種擁有關係就被解除,互斥對象重新進入發信號狀態。
   注意除WaitForSingleObject()函數外,你還可以使用WaitForMultipleObject()和MsgWaitForMultipleObject()函數,它們可以等待幾個對象變爲發信號狀態。這兩個函數的詳細情況請看Win32 API聯機文檔。
   3. 信號量 
   另一種使線程同步的技術是使用信號量對象。它是在互斥的基礎上建立的,但信號量增加了資源計數的功能,預定數目的線程允許同時進入要同步的代碼。可以用CreateSemaphore()來創建一個信號量對象,其聲明如下:
   function
   和CreateMutex()函數一樣,CreateSemaphore()的第一個參數也是一個指向TSecurityAttribute s記錄的指針,此參數的缺省值可以設爲nil。
   lInitialCount參數用來指定一個信號量的初始計數值,這個值必須在0和lMaximumCount之間。此參數大於0,就表示信號量處於發信號狀態。當調用WaitForSingleObject()函數(或其他函數)時,此計數值就減1。當調用ReleaseSemaphore()時,此計數值加1。
   參數lMaximumCount指定計數值的最大值。如果這個信號量代表某種資源,那麼這個值代表可用資源總數。
   參數lpName用於給出信號量對象的名稱,它類似於CreateMutex()函數的lpName參數。
   ——————————————————————————————————————————


   線程同步: 
   Synchronize()是在一個隱蔽的窗口裏運行,如果在這裏你的任務很繁忙,你的主窗口會阻塞掉;Synchronize()只是將該線程的代碼放到主線程中運行,並非線程同步。
   臨界區是一個進程裏的所有線程同步的最好辦法,他不是系統級的,只是進程級的,也就是說他可能利用進程內的一些標誌來保證該進程內的線程同步,據Richter說是一個記數循環;臨界區只能在同一進程內使用;臨界區只能無限期等待,不過2k增加了TryEnterCriticalSection函數實現0時間等待。 
   互斥則是保證多進程間的線程同步,他是利用系統內核對象來保證同步的。由於系統內核對象可以是有名字的,因此多個進程間可以利用這個有名字的內核對象保證系統資源的線程安全性。互斥量是Win32 內核對象,由操作系統負責管理;互斥量可以使用WaitForSingleObject實現無限等待,0時間等待和任意時間等待。

   1. 臨界區 
   臨界區是一種最直接的線程同步方式。所謂臨界區,就是一次只能由一個線程來執行的一段代碼。如果把初始化數組的代碼放在臨界區內,另一個線程在第一個線程處理完之前是不會被執行的。在使用臨界區之前,必須使用InitializeCriticalSection()過程來初始化它。
   在第一個線程調用了EnterCriticalSection()之後,所有別的線程就不能再進入代碼塊。下一個線程要等第一個線程調用LeaveCriticalSection()後才能被喚醒。


   2. 互斥 
   互斥非常類似於臨界區,除了兩個關鍵的區別:首先,互斥可用於跨進程的線程同步。其次,互斥能被賦予一個字符串名字,並且通過引用此名字創建現有互斥對象的附加句柄。
   提示:臨界區與事件對象(比如互斥對象)的最大的區別是在性能上。臨界區在沒有線程衝突時,要用10 ~ 15個時間片,而事件對象由於涉及到系統內核要用400~600個時間片。
   當一個互斥對象不再被一個線程所擁有,它就處於發信號狀態。此時首先調用WaitForSingleObject()函數的線程就成爲該互斥對象的擁有者,此互斥對象設爲不發信號狀態。當線程調用ReleaseMutex()函數並傳遞一個互斥對象的句柄作爲參數時,這種擁有關係就被解除,互斥對象重新進入發信號狀態。
   可以調用函數CreateMutex()來創建一個互斥量。當使用完互斥對象時,應當調用CloseHandle()來關閉它。
   3. 信號量


   另一種使線程同步的技術是使用信號量對象。它是在互斥的基礎上建立的,但信號量增加了資源計數的功能,預定數目的線程允許同時進入要同步的代碼。可以用CreateSemaphore()來創建一個信號量對象,
   因爲只允許一個線程進入要同步的代碼,所以信號量的最大計數值(lMaximumCount)要設爲1。ReleaseSemaphore()函數將使信號量對象的計數加1;
   記住,最後一定要調用CloseHandle()函數來釋放由CreateSemaphore()創建的信號量對象的句柄。
   ★★★WaitForSingleObject函數的返值: 
   WAIT_ABANDONED指定的對象是互斥對象,並且擁有這個互斥對象的線程在沒有釋放此對象之前就已終止。此時就稱互斥對象被拋棄。這種情況下,這個互斥對象歸當前線程所有,並把它設爲非發信號狀態;
   WAIT_OBJECT_0 指定的對象處於發信號狀態;
   WAIT_TIMEOUT等待的時間已過,對象仍然是非發信號狀態;
   ——————————————————————————————————————————————


   VCL支持三種技術來達到這個目的:
   (2) 使用critical區


   如果對象沒有提高內置的鎖定功能,需要使用critical區,Critical區在同一個時間只也許一個線程進入。爲了使用Critical區,產生一個TCriticalSection全局的實例。TcriticalSection有兩個方法,Acquire(阻止其他線程執行該區域)和Release(取消阻止)
   每個Critical區是與你想要保護的全局內存相關聯。每個訪問全局內存的線程必須首先使用Acquire來保證沒有其他線程使用它。完成以後,線程調用Release方法,讓其他線程也可以通過調用Acquire來使用這塊全局內存。 
   警告:Critical區只有在所有的線程都使用它來訪問全局內存,如果有線程直接調用內存,而不通過Acquire,會造成同時訪問的問題。例如:LockXY是一個全局的Critical區變量。任何一個訪問全局X, Y的變量的線程,在訪問前,都必須使用Acquire 
   LockXY.Acquire;{ lock out other threads } 
   try 
   Y := sin(X); 
   finally 
   LockXY.Release;
   end
   臨界區主要是爲實現線程之間同步的,但是使用的時候注意,一定要在用此臨界對象同步的線程之外建立該對象(一般在主線程中建立臨界對象)。 
   ————————————————————————————————————————————————
   線程同步使用臨界區,進程同步使用互斥對象。


   Delphi中封裝了臨界對象。對象名爲TCriticalSection,使用的時候只要在主線程當中建立這個臨界對象(注意一定要在需要同步的線程之外建立這個對象)。具體同步的時候使用Lock和Unlock即可。


   而進程間同步建立互斥對象,則只需要建立一個互斥對象CreateMutex. 需要同步的時候只需要WaitForSingleObject(mutexhandle, INFINITE) unlock的時候只需要ReleaseMutex(mutexhandle);即可。


   有很多方法, 信號燈, 臨界區, 互斥對象,此外, windows下還可以用全局原子,共享內存等等. 在windows體系中, 讀寫一個8位整數時原子的, 你可以依靠這一點完成互斥的方法. 對於能夠產生全局名稱的方法能夠可以在進程間同步上(如互斥對象), 也可以用在線程間同步上;不能夠產生全局名稱的方法(如臨界區)只能用在線程間同步上.

 

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章