首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> Delphi

多線程程式設計(8):CriticalSection 臨界區同步詳解

     編輯:Delphi

為什麼需要線程同步?

多線程同時存取共享資源(如 TListBox.Items、Canvas、全域變數)會導致競態條件:資料損壞、介面錯亂、甚至程式崩潰。下圖展示三條線程無同步地向 ListBox 寫入 0~99,結果順序混亂、重複、遺漏:

無同步時的 ListBox 輸出亂序

CriticalSection(臨界區)核心概念

臨界區是使用者模式的輕量級同步物件,不屬於核心對象,因此:

  • 速度極快(無核心模式切換開銷)
  • 無句柄、無安全屬性、不可跨進程(僅限進程內線程)
  • 適用於高頻、短暫的共享資源保護

API 級用法

var CS: TRTLCriticalSection; // 全域結構體變數

// 初始化 (程式啟動/表單建立時)
InitializeCriticalSection(CS);

// 進入臨界區 (獨佔)
EnterCriticalSection(CS);
try
  // 受保護的共享資源操作
finally
  LeaveCriticalSection(CS); // 離開臨界區
end;

// 刪除 (程式結束/表單銷毀時)
DeleteCriticalSection(CS);

注意:EnterCriticalSection 會無限等待;若需非阻塞嘗試,可用 TryEnterCriticalSection(立即返回 Boolean)。

完整範例:三條線程順序寫入 ListBox

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);
  try
    for i := 0 to 99 do
      Form1.ListBox1.Items.Add(IntToStr(i));
  finally
    LeaveCriticalSection(CS);
  end;
  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.

執行效果

CriticalSection 同步後輸出整齊

三組 0~99 依序完整輸出,無交織、無遺漏,證明臨界區成功序列化了 ListBox 存取。

SyncObjs.TCriticalSection 封裝類

Delphi SyncObjs 單元提供物件導向封裝,用法更直觀、自動管理生命週期:

uses SyncObjs;
var CS: TCriticalSection;

// 建立
CS := TCriticalSection.Create;

// 使用
CS.Enter;
try
  // 共享資源操作
finally
  CS.Leave;
end;

// 釋放
CS.Free;

內部同樣呼叫 InitializeCriticalSection/DeleteCriticalSection,效能極接近 API 直呼。

CriticalSection vs Mutex 對比

特性CriticalSectionMutex
層級使用者模式核心模式
速度極快 (無上下文切換)較慢 (需核心切換)
跨進程不支援支援 (具名)
等待行為自旋鎖 + 核心事件純核心等待
遺棄處理無 (擁有者崩潰會導致永久鎖死)有 (WAIT_ABANDONED)
適用場景高頻、短關鍵區、進程內跨進程、長操作、需遺棄檢測

最佳實踐建議

  • 儘量使用 CriticalSection:除非需要跨進程同步,否則它是首選。
  • 關鍵區要短:只保護必要的共享存取,避免在關鍵區內呼叫耗時 API、Sleep、I/O。
  • 配對 Enter/Leave:務必用 try...finally 確保釋放,防止死結。
  • 避免巢狀鎖:若必須多鎖,嚴格遵守固定獲取順序,防止死結。
  • 初始化/刪除時機:在所有線程啟動前初始化,所有線程結束後刪除。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved