多執行緒同步之 CriticalSection(臨界區)
阿新 • • 發佈:2019-02-19
先看一段程式, 程式碼檔案:
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) ListBox1: TListBox; Button1: TButton; procedure FormCreate(Sender: TObject); procedure Button1Click(Sender: TObject); end; var Form1: TForm1; implementation {$R *.dfm} function MyThreadFun(p: Pointer): DWORD; stdcall; var i: Integer; begin for i := 0 to 99 do Form1.ListBox1.Items.Add(IntToStr(i)); Result := 0; end; procedure TForm1.Button1Click(Sender: TObject); var ID: DWORD; begin CreateThread(nil, 0, @MyThreadFun, nil, 0, ID); CreateThread(nil, 0, @MyThreadFun, nil, 0, ID); CreateThread(nil, 0, @MyThreadFun, nil, 0, ID); end; procedure TForm1.FormCreate(Sender: TObject); begin ListBox1.Align := alLeft; end; end.
窗體檔案:
object Form1: TForm1 Left = 0 Top = 0 Caption = 'Form1' ClientHeight = 154 ClientWidth = 214Color = clBtnFace Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -11 Font.Name = 'Tahoma' Font.Style = [] OldCreateOrder = False OnCreate = FormCreate PixelsPerInch = 96 TextHeight = 13 object ListBox1: TListBox Left = 9 Top = 9 Width = 121 Height = 97 ItemHeight = 13 TabOrder = 0 end object Button1: TButton Left = 131 Top = 112 Width = 75 Height = 25 Caption = 'Button1' TabOrder = 1 OnClick = Button1Click end end
在這段程式中, 有三個執行緒幾乎是同時建立, 向窗體中的 ListBox1 中寫資料, 最後寫出的結果是這樣的:
能不能讓它們別打架, 一個完了另一個再來? 這就要用到多執行緒的同步技術.
前面說過, 最簡單的同步手段就是 "臨界區".
先說這個 "同步"(Synchronize), 首先這個名字起的不好, 我們好像需要的是 "非同步"; 其實非同步也不準確...
管它叫什麼名字呢, 它的目的就是保證不衝突、有次序、都發生.
"臨界區"(CriticalSection): 當把一段程式碼放入一個臨界區, 執行緒執行到臨界區時就獨佔了, 讓其他也要執行此程式碼的執行緒先等等; 這和前面用的 Lock 和 UnLock 差不多; 使用格式如下:
var CS: TRTLCriticalSection; {宣告一個 TRTLCriticalSection 結構型別變數; 它應該是全域性的} InitializeCriticalSection(CS); {初始化} EnterCriticalSection(CS); {開始: 輪到我了其他執行緒走開} LeaveCriticalSection(CS); {結束: 其他執行緒可以來了} DeleteCriticalSection(CS); {刪除: 注意不能過早刪除} //也可用 TryEnterCriticalSection 替代 EnterCriticalSection.
用上臨界區, 重寫上面的程式碼, 執行效果圖:
程式碼檔案:
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) ListBox1: TListBox; Button1: TButton; procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure Button1Click(Sender: TObject); end; var Form1: TForm1; implementation {$R *.dfm} var CS: TRTLCriticalSection; function MyThreadFun(p: Pointer): DWORD; stdcall; var i: Integer; begin EnterCriticalSection(CS); for i := 0 to 99 do Form1.ListBox1.Items.Add(IntToStr(i)); LeaveCriticalSection(CS); Result := 0; end; procedure TForm1.Button1Click(Sender: TObject); var ID: DWORD; begin CreateThread(nil, 0, @MyThreadFun, nil, 0, ID); CreateThread(nil, 0, @MyThreadFun, nil, 0, ID); CreateThread(nil, 0, @MyThreadFun, nil, 0, ID); end; procedure TForm1.FormCreate(Sender: TObject); begin ListBox1.Align := alLeft; InitializeCriticalSection(CS); end; procedure TForm1.FormDestroy(Sender: TObject); begin DeleteCriticalSection(CS); end; end.
Delphi 在 SyncObjs 單元給封裝了一個 TCriticalSection 類, 用法差不多, 程式碼如下:
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) ListBox1: TListBox; Button1: TButton; procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure Button1Click(Sender: TObject); end; var Form1: TForm1; implementation {$R *.dfm} uses SyncObjs; var CS: TCriticalSection; function MyThreadFun(p: Pointer): DWORD; stdcall; var i: Integer; begin CS.Enter; for i := 0 to 99 do Form1.ListBox1.Items.Add(IntToStr(i)); CS.Leave; Result := 0; end; procedure TForm1.Button1Click(Sender: TObject); var ID: DWORD; begin CreateThread(nil, 0, @MyThreadFun, nil, 0, ID); CreateThread(nil, 0, @MyThreadFun, nil, 0, ID); CreateThread(nil, 0, @MyThreadFun, nil, 0, ID); end; procedure TForm1.FormCreate(Sender: TObject); begin ListBox1.Align := alLeft; CS := TCriticalSection.Create; end; procedure TForm1.FormDestroy(Sender: TObject); begin CS.Free; end; end.