1. 程式人生 > >多執行緒同步之 CriticalSection(臨界區)

多執行緒同步之 CriticalSection(臨界區)


先看一段程式, 程式碼檔案:
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 = 214
  Color = 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.