日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Delphi关于多线程同步的一些方法

發布時間:2023/12/2 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Delphi关于多线程同步的一些方法 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
(注:本文為轉載? http://hi.baidu.com/navy1130/blog/item/468fcdc448794fce38db49ee.html)
線程是進程內一個相對獨立的、可調度的執行單元。一個應用可以有一個主線程,一個主線程可以有多個子線程,子線程還可以有自己的子線程,這樣就構成了多線程應用了。由于多個線程往往會同時訪問同一塊內存區域,頻繁的訪問這塊區域,將會增加產生線程沖突的概率。一旦產生了沖突,將會造成不可預料的結果(比如:該公用區域的值是不可預料的),可見處理線程同步的必要性。
   (注意:本文中出現的所有代碼都是用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; //定義一個線程內公共變量,接收從進程傳進來的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可視組件(本例中,這個同步可以不要,見Button1Click);
  ??? 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;//不同的VCL控件;
  ??? th2.LBox:=listBox2;//不同的VCL控件;
  ??? th1.Resume;//開始執行
  ??? th2.Resume;
   end;
   end.
  
   由上例可見,當多個線程同時修改一個公用變量時,會產生沖突,所以我們要設法防止它,這樣我們開發的多線程應用才能夠穩定地運行。下面我們來改進它。我們先使用臨界段來串行化,實現同步。
???? 在上例unit1.pas代碼的uses段中加入SyncObjs單元,
???? 加入全局臨界段變量:Critical1:TRTLCriticalSection
???? 在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:TRRLCriticalSection);stdcall;
   正如你所想的,參數lpCriticalSection就是由InitializeCriticalSection()填充的記錄。
   當你不需要TRTLCriticalSection記錄時,應當調用DeleteCriticalSection()過程,下面是它的聲明:
   procedure DeleteCriticalSection(var lpCriticalSection:TRRLCriticalSection);stdcall;
  
   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時間等待。
   互斥則是保證多進程間的線程同步,他是利用系統內核對象來保證同步的。由于系統內核對象可以是有名字的,因此多個進程間可以利用

這個有名字的內核對象保證系統資源的線程安全性?;コ饬渴荳in32 內核對象,由操作系統負責管理;互斥量可以使用WaitForSingleObject實

現無限等待,0時間等待和任意時間等待。
   1. 臨界區
   臨界區是一種最直接的線程同步方式。所謂臨界區,就是一次只能由一個線程來執行的一段代碼。如果把初始化數組的代碼放在臨界區內

,另一個線程在第一個線程處理完之前是不會被執行的。在使用臨界區之前,必須使用InitializeCriticalSection()過程來初始化它。
   在第一個線程調用了EnterCriticalSection()之后,所有別的線程就不能再進入代碼塊。下一個線程要等第一個線程調用

LeaveCriticalSection()后才能被喚醒。
   2. 互斥
   互斥非常類似于臨界區,除了兩個關鍵的區別:首先,互斥可用于跨進程的線程同步。其次,互斥能被賦予一個字符串名字,并且通過引

用此名字創建現有互斥對象的附加句柄。
   提示:臨界區與事件對象(比如互斥對象)的最大的區別是在性能上。臨界區在沒有線程沖突時,要用10 ~ 15個時間片,而事件對象由于

涉及到系統內核要用400~600個時間片。
   當一個互斥對象不再被一個線程所擁有,它就處于發信號狀態。此時首先調用WaitForSingleObject()函數的線程就成為該互斥對象的擁有

者,此互斥對象設為不發信號狀態。當線程調用ReleaseMutex()函數并傳遞一個互斥對象的句柄作為參數時,這種擁有關系就被解除,互斥對

象重新進入發信號狀態。
   可以調用函數CreateMutex()來創建一個互斥量。當使用完互斥對象時,應當調用CloseHandle()來關閉它。
   3. 信號量
   另一種使線程同步的技術是使用信號量對象。它是在互斥的基礎上建立的,但信號量增加了資源計數的功能,預定數目的線程允許同時進

入要同步的代碼??梢杂肅reateSemaphore()來創建一個信號量對象,
   因為只允許一個線程進入要同步的代碼,所以信號量的最大計數值(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位整數時原

子的, 你可以依靠這一點完成互斥的方法. 對于能夠產生全局名稱的方法能夠可以在進程間同步上(如互斥對象), 也可以用在線程間同步上;不

能夠產生全局名稱的方法(如臨界區)只能用在線程間同步上.

轉載于:https://www.cnblogs.com/bbinking/archive/2010/03/10/1682802.html

創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

總結

以上是生活随笔為你收集整理的Delphi关于多线程同步的一些方法的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。