日本好好热aⅴ|国产99视频精品免费观看|日本成人aV在线|久热香蕉国产在线

  • <cite id="ikgdy"><table id="ikgdy"></table></cite>
    1. 西西軟件園多重安全檢測(cè)下載網(wǎng)站、值得信賴的軟件下載站!
      軟件
      軟件
      文章
      搜索

      首頁編程開發(fā)Delphi → Delphi 中多線程同步的一些處理方法

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

      前往專題相關(guān)軟件相關(guān)文章發(fā)表評(píng)論 來源:西西整理時(shí)間:2012/8/29 15:40:21字體大。A-A+

      作者:西西點(diǎn)擊:637次評(píng)論:10次標(biāo)簽: delphi

      當(dāng)創(chuàng)建了多個(gè)線程,并且多個(gè)線程都要訪問同一資源,,就有可能出現(xiàn)混亂,于是用Synchronize來控制,使同一時(shí)間只有一個(gè)線程使用那部分資源,Synchronize參數(shù)里面的代碼就是多線程需要公共的代碼!


      線程是進(jìn)程內(nèi)一個(gè)相對(duì)獨(dú)立的、可調(diào)度的執(zhí)行單元。一個(gè)應(yīng)用可以有一個(gè)主線程,一個(gè)主線程可以有多個(gè)子線程,子線程還可以有自己的子線程,這樣就構(gòu)成了多線程應(yīng)用了。由于多個(gè)線程往往會(huì)同時(shí)訪問同一塊內(nèi)存區(qū)域,頻繁的訪問這塊區(qū)域,將會(huì)增加產(chǎn)生線程沖突的概率。一旦產(chǎn)生了沖突,將會(huì)造成不可預(yù)料的結(jié)果(該公用區(qū)域的值是不可預(yù)料的)可見處理線程同步的必要性。
         注意:本文中出現(xiàn)的所有代碼都是用DELPHI描述的,調(diào)試環(huán)境為Windows me ,Delphi 6。其中所涉及的Windows API函數(shù)可以從MSDN獲得詳細(xì)的文檔。
         首先引用一個(gè)實(shí)例來引出我們以下的討論,該實(shí)例沒有采取任何措施來避免線程沖突,它的主要過程為:由主線程啟動(dòng)兩個(gè)線程對(duì)letters這個(gè)全局變量進(jìn)行頻繁的讀寫,然后分別把修改的結(jié)果顯示到ListBox中。由于沒有同步這兩個(gè)線程,使得線程在修改letters時(shí)產(chǎn)生了不可預(yù)料的結(jié)果。
         ListBox中的每一行的字母都應(yīng)該一致,但是上圖畫線處則不同,這就是線程沖突產(chǎn)生的結(jié)果。當(dāng)兩個(gè)線程同時(shí)訪問該共享內(nèi)存時(shí),一個(gè)線程還未對(duì)該內(nèi)存修改完,另一個(gè)線程又對(duì)該內(nèi)存進(jìn)行了修改,由于寫值的過程沒有被串行化,這樣就產(chǎn)生了無效的結(jié)果?梢娋程同步的重要性。

         以下是本例的代碼


         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}
        
         //線程類實(shí)現(xiàn)部分
         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//循環(huán)1000次增加產(chǎn)生沖突的幾率
         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;
        
         //窗口類實(shí)現(xiàn)部分
         procedure TForm1.Button1Click(Sender: TObject);
         var
         th1,th2:TListThread;
         begin
         Listbox1.Clear;
         Listbox2.Clear;
         th1:=tlistThread.Create(true);//創(chuàng)建線程1
         th2:=tlistThread.Create(true);//創(chuàng)建線程2
         th1.LBox:=listBox1;
         th2.LBox:=listBox2;
         th1.Resume;//開始執(zhí)行
         th2.Resume;
         end;
         end.
        
         由上例可見,當(dāng)多個(gè)線程同時(shí)修改一個(gè)公用變量時(shí),會(huì)產(chǎn)生沖突,所以我們要設(shè)法防止它,這樣我們開發(fā)的多線程應(yīng)用才能夠穩(wěn)定地運(yùn)行。下面我們來改進(jìn)它。我們先使用臨界段來串行化,實(shí)現(xiàn)同步。在上例unit1.pas代碼的uses段中加入SyncObjs單元,加入全局臨界段變量(TRTLCriticalSection)Critical1,在FormCreate事件中加入InitializeCriticalSection(Critical1)這句代碼,在FormDestroy事件中加入DeleteCriticalSection(Critical1)這句代碼,然后修改TListThread.Execute函數(shù),修改后的代碼似如下所示(?處為增加的代碼):
         procedure TListThread.Execute;
         var
         I,J,K:Integer;
         begin
         for i:=0 to 50 do
         begin
         ?EnterCriticalSection(Critical1);//進(jìn)入臨界段
         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;
         好了,重新編譯,運(yùn)行結(jié)果如下圖所示(略)
        
         程序成功的避免了沖突,看來真的很簡(jiǎn)單,我們成功了!當(dāng)然我們還可以使用其它同步技術(shù)如Mutex(互斥對(duì)象), Semaphore(信號(hào)量)等,這些技術(shù)都是Windows通過API直接提供給我們的。


        
         下面總結(jié)一下Windows常用的幾種線程同步技術(shù)。


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


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


         線程同步:
         Synchronize()是在一個(gè)隱蔽的窗口里運(yùn)行,如果在這里你的任務(wù)很繁忙,你的主窗口會(huì)阻塞掉;Synchronize()只是將該線程的代碼放到主線程中運(yùn)行,并非線程同步。
         臨界區(qū)是一個(gè)進(jìn)程里的所有線程同步的最好辦法,他不是系統(tǒng)級(jí)的,只是進(jìn)程級(jí)的,也就是說他可能利用進(jìn)程內(nèi)的一些標(biāo)志來保證該進(jìn)程內(nèi)的線程同步,據(jù)Richter說是一個(gè)記數(shù)循環(huán);臨界區(qū)只能在同一進(jìn)程內(nèi)使用;臨界區(qū)只能無限期等待,不過2k增加了TryEnterCriticalSection函數(shù)實(shí)現(xiàn)0時(shí)間等待。
         互斥則是保證多進(jìn)程間的線程同步,他是利用系統(tǒng)內(nèi)核對(duì)象來保證同步的。由于系統(tǒng)內(nèi)核對(duì)象可以是有名字的,因此多個(gè)進(jìn)程間可以利用這個(gè)有名字的內(nèi)核對(duì)象保證系統(tǒng)資源的線程安全性;コ饬渴荳in32 內(nèi)核對(duì)象,由操作系統(tǒng)負(fù)責(zé)管理;互斥量可以使用WaitForSingleObject實(shí)現(xiàn)無限等待,0時(shí)間等待和任意時(shí)間等待。

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


         2. 互斥
         互斥非常類似于臨界區(qū),除了兩個(gè)關(guān)鍵的區(qū)別:首先,互斥可用于跨進(jìn)程的線程同步。其次,互斥能被賦予一個(gè)字符串名字,并且通過引用此名字創(chuàng)建現(xiàn)有互斥對(duì)象的附加句柄。
         提示:臨界區(qū)與事件對(duì)象(比如互斥對(duì)象)的最大的區(qū)別是在性能上。臨界區(qū)在沒有線程沖突時(shí),要用10 ~ 15個(gè)時(shí)間片,而事件對(duì)象由于涉及到系統(tǒng)內(nèi)核要用400~600個(gè)時(shí)間片。
         當(dāng)一個(gè)互斥對(duì)象不再被一個(gè)線程所擁有,它就處于發(fā)信號(hào)狀態(tài)。此時(shí)首先調(diào)用WaitForSingleObject()函數(shù)的線程就成為該互斥對(duì)象的擁有者,此互斥對(duì)象設(shè)為不發(fā)信號(hào)狀態(tài)。當(dāng)線程調(diào)用ReleaseMutex()函數(shù)并傳遞一個(gè)互斥對(duì)象的句柄作為參數(shù)時(shí),這種擁有關(guān)系就被解除,互斥對(duì)象重新進(jìn)入發(fā)信號(hào)狀態(tài)。
         可以調(diào)用函數(shù)CreateMutex()來創(chuàng)建一個(gè)互斥量。當(dāng)使用完互斥對(duì)象時(shí),應(yīng)當(dāng)調(diào)用CloseHandle()來關(guān)閉它。
         3. 信號(hào)量


         另一種使線程同步的技術(shù)是使用信號(hào)量對(duì)象。它是在互斥的基礎(chǔ)上建立的,但信號(hào)量增加了資源計(jì)數(shù)的功能,預(yù)定數(shù)目的線程允許同時(shí)進(jìn)入要同步的代碼?梢杂肅reateSemaphore()來創(chuàng)建一個(gè)信號(hào)量對(duì)象,
         因?yàn)橹辉试S一個(gè)線程進(jìn)入要同步的代碼,所以信號(hào)量的最大計(jì)數(shù)值(lMaximumCount)要設(shè)為1。ReleaseSemaphore()函數(shù)將使信號(hào)量對(duì)象的計(jì)數(shù)加1;
         記住,最后一定要調(diào)用CloseHandle()函數(shù)來釋放由CreateSemaphore()創(chuàng)建的信號(hào)量對(duì)象的句柄。
         ★★★WaitForSingleObject函數(shù)的返值:
         WAIT_ABANDONED指定的對(duì)象是互斥對(duì)象,并且擁有這個(gè)互斥對(duì)象的線程在沒有釋放此對(duì)象之前就已終止。此時(shí)就稱互斥對(duì)象被拋棄。這種情況下,這個(gè)互斥對(duì)象歸當(dāng)前線程所有,并把它設(shè)為非發(fā)信號(hào)狀態(tài);
         WAIT_OBJECT_0 指定的對(duì)象處于發(fā)信號(hào)狀態(tài);
         WAIT_TIMEOUT等待的時(shí)間已過,對(duì)象仍然是非發(fā)信號(hào)狀態(tài);
         ——————————————————————————————————————————————


         VCL支持三種技術(shù)來達(dá)到這個(gè)目的:
         (2) 使用critical區(qū)


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


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


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


         有很多方法, 信號(hào)燈, 臨界區(qū), 互斥對(duì)象,此外, windows下還可以用全局原子,共享內(nèi)存等等. 在windows體系中, 讀寫一個(gè)8位整數(shù)時(shí)原子的, 你可以依靠這一點(diǎn)完成互斥的方法. 對(duì)于能夠產(chǎn)生全局名稱的方法能夠可以在進(jìn)程間同步上(如互斥對(duì)象), 也可以用在線程間同步上;不能夠產(chǎn)生全局名稱的方法(如臨界區(qū))只能用在線程間同步上.

       

      在編寫多線程應(yīng)用程序時(shí),最重要的是控制好線程間的同步資源訪問,以保證線程的安全運(yùn)行。Win 32 API提供了一組同步對(duì)象,如:信號(hào)燈(Semaphore)、互斥(Mutex)、臨界區(qū)(CriticalSection)和事件(Event)等,用來解決這個(gè)問題。 

        Delphi分別將事件對(duì)象和臨界區(qū)對(duì)象封裝為Tevent對(duì)象和TcritialSection對(duì)象,使得這兩個(gè)對(duì)象的使用簡(jiǎn)單且方便。但是如果在Delphi程序中要使用信號(hào)燈或互斥等對(duì)象就必須借助于復(fù)雜的Win32 API函數(shù),這對(duì)那些不熟悉Win32 API函數(shù)的編程人員來說很不方便。因此,筆者用Delphi構(gòu)造了兩個(gè)類,對(duì)信號(hào)燈和互斥對(duì)象進(jìn)行了封裝(分別為TSemaphore和TMutex),希望對(duì)廣大Delphi編程人員有所幫助。 

        一、類的構(gòu)造 
        我們先對(duì)Win32 API的信號(hào)燈對(duì)象和互斥對(duì)象進(jìn)行抽象,構(gòu)造一個(gè)父類THandleObjectEx,然后由這個(gè)父類派生出兩個(gè)子類Tsemphore和Tmutex。 

        類的源代碼如下: 

        unit SyncobjsEx; 

        interface 

        uses 
      Windows,Messages,SysUtils,Classes,Syncobjs; 

        type 

         THandleObjectEx = class(THandleObject) 

        // THandleObjectEx為互斥類和信號(hào)燈類的父類 

         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) 

        //信號(hào)燈類 

        public 

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

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

         end; 

        implementation 

        { THandleObjectEx }//父類的實(shí)現(xiàn) 

        destructor THandleObjectEx.Destroy; 

        begin 

         
      Windows.CloseHandle(FHandle); 

         inherited Destroy; 

        end; 

        procedure THandleObjectEx.Release; 

        begin 

        end; 

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

        //等待函數(shù),參數(shù)為等待時(shí)間 

        begin 

        case WaitForSingleObject(Handle, Timeout) of 

        WAIT_ABANDONED: Result := wrAbandoned; 

        //無信號(hào) 

        WAIT_OBJECT_0: Result := wrSignaled; 

        //有信號(hào) 

        WAIT_TIMEOUT: Result := wrTimeout;//超時(shí) 

        WAIT_FAILED://失敗 

         begin 

         Result := wrError; 

         FLastError := GetLastError; 

         end; 

         else 

         Result := wrError; 

         end; 

        end; 

        { TSemaphore }//信號(hào)燈類的實(shí)現(xiàn) 

        constructor TSemaphore.Create(SemaphoreAttributes: PSecurityAttributes; 

         InitialCount, MaximumCount: integer; const Name: string);//信號(hào)燈類的構(gòu)造函數(shù) 

        begin 

        FHandle := CreateSemaphore 

        (SemaphoreAttributes,InitialCount, 

        MaximumCount,PChar(Name)); 

        //四個(gè)參數(shù)分別為:安全屬性、初始信號(hào)燈計(jì)數(shù)、最大信號(hào)燈計(jì)數(shù)、信號(hào)燈名字 

        end; 

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

        //信號(hào)燈類的Release方法,每執(zhí)行一次按指定量增加信號(hào)燈計(jì)數(shù) 

        begin 

         
      Windows.ReleaseSemaphore(FHandle, ReleaseCount, PreviousCount); 

        end; 

        { TMutex }//互斥類的實(shí)現(xiàn) 

        constructor TMutex.Create(MutexAttributes: PSecurityAttributes; 

        InitialOwner: Boolean; const Name: string); 

        //互斥類的構(gòu)造函數(shù) 

        begin 

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

        end; 

        procedure TMutex.Release;//互斥類的Release方法,用來釋放對(duì)互斥對(duì)象的所有權(quán) 

        begin 

         
      Windows.ReleaseMutex(FHandle); 

        end; 

        end. 

        二、信號(hào)燈對(duì)象與互斥對(duì)象的使用 
        1. 信號(hào)燈對(duì)象 

        信號(hào)燈對(duì)象維持一個(gè)從0到指定最大值之間的數(shù)。在其計(jì)數(shù)大于0時(shí)是有信號(hào)的,而在其計(jì)數(shù)為0時(shí)是無信號(hào)的。信號(hào)燈對(duì)象可用來限制對(duì)共享資源進(jìn)行訪問的線程數(shù)量,例如應(yīng)用程序可使用信號(hào)燈對(duì)象來限制它建立的窗口數(shù)量。 

        用類的Create方法來建立信號(hào)燈對(duì)象,在調(diào)用該方法時(shí),可以指定對(duì)象的初始計(jì)數(shù)和最大計(jì)數(shù)。該方法有四個(gè)參數(shù),依次為:安全屬性、初始計(jì)數(shù)、最大計(jì)數(shù)和對(duì)象名字(以便別的進(jìn)程的線程可打開指定名字的信號(hào)燈句柄)。如: 

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

        一般把信號(hào)燈的初始計(jì)數(shù)設(shè)置成最大值。每次當(dāng)信號(hào)燈有信號(hào)并等待函數(shù)返回時(shí),信號(hào)燈計(jì)數(shù)就會(huì)減1,而通過調(diào)用對(duì)象的Release方法可按指定量增加信號(hào)燈的計(jì)數(shù)(默認(rèn)為加1)。計(jì)數(shù)值越小就表明訪問共享資源的程序越多。如:“Semaphore.Release(3, nil);”,其中第一個(gè)參數(shù)為增加的信號(hào)燈數(shù)量,第二個(gè)參數(shù)為執(zhí)行該方法之前的信號(hào)燈數(shù)量。信號(hào)燈用法舉例: 

        if wrSignaled = Semaphore.WaitFor(10000) then//若信號(hào)燈是有信號(hào)的 

        begin 

         //打開另一個(gè)窗口 

        end 

         Semaphore.Release() 

        在線程建立窗口之前,它使用WaitFor函數(shù)確定信號(hào)燈的當(dāng)前計(jì)數(shù)是否允許建立新的窗口,等待時(shí)間設(shè)為10秒。 

        2. 互斥對(duì)象 

        Mutex對(duì)象的狀態(tài)在它不被任何線程擁有時(shí)是有信號(hào)的,而當(dāng)它被擁有時(shí)則是無信號(hào)的。Mutex對(duì)象很適合用來協(xié)調(diào)多個(gè)線程對(duì)共享資源的互斥訪問(mutually exclusive)。例如,有幾個(gè)線程共享對(duì)數(shù)據(jù)庫的訪問時(shí),線程可以使用Mutex對(duì)象,一次只允許一個(gè)線程向數(shù)據(jù)庫寫入。 

        用類的Create方法建立Mutex 對(duì)象,在建立Mutex 時(shí),可以為對(duì)象起個(gè)名字,這樣其他進(jìn)程中的線程可以打開指定名字的Mutex對(duì)象句柄。例如: 

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

        在完成對(duì)共享資源的訪問后,可以調(diào)用Release方法來釋放Mutex,以便讓別的線程能訪問共享資源。如果線程終止而不釋放Mutex,則認(rèn)為該Mutex被廢棄。 

        互斥對(duì)象用法舉例如下: 

        if wrSignaled = Mutex.WaitFor(10000) then//若獲得互斥對(duì)象的擁有權(quán) 

         begin 

         try 

         //往數(shù)據(jù)庫寫入 

         finally 

        Mutex.Release;//釋放對(duì)互斥對(duì)象的擁有權(quán) 

         end; 

         end; 

       

       

        相關(guān)評(píng)論

        閱讀本文后您有什么感想? 已有人給出評(píng)價(jià)!

        • 8 喜歡喜歡
        • 3 頂
        • 1 難過難過
        • 5 囧
        • 3 圍觀圍觀
        • 2 無聊無聊

        熱門評(píng)論

        最新評(píng)論

        發(fā)表評(píng)論 查看所有評(píng)論(10)

        昵稱:
        表情: 高興 可 汗 我不要 害羞 好 下下下 送花 屎 親親
        字?jǐn)?shù): 0/500 (您的評(píng)論需要經(jīng)過審核才能顯示)