程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Delphi >> 關於線程同步的一些方法

關於線程同步的一些方法

編輯:Delphi
線程是進程內一個相對獨立的、可調度的執行單元。一個應用可以有一個主線程,一個主線程可以有多個子線程,子線程還可以有自己的子線程,這樣就構成了多線程應用了。由於多個線程往往會同時訪問同一塊內存區域,頻繁的訪問這塊區域,將會增加產生線程沖突的概率。一旦產生了沖突,將會造成不可預料的結果(該公用區域的值是不可預料的)可見處理線程同步的必要性。   注意:本文中出現的所有代碼都是用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中。   當你開發多線程應用時,並且多個線程同時訪問一個共享資源或數據時,你需要考慮線程同步的問題了。
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved