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

Delphi 多線程程式設計 (12):Semaphore 信號量 - 多資源並發同步完整指南

     編輯:Delphi

Semaphore (信號量/信號燈) 是支援**多資源並發存取**的核心同步物件,類比「銀行多櫃台服務」:計數器表示可用資源數,線程等待獲取信號 (WaitForSingleObject),用畢釋放 (ReleaseSemaphore)。本文完整解析參數、API 版與 TSemaphore 封裝類雙實現。

CreateSemaphore 參數詳解

function CreateSemaphore(
  lpSemaphoreAttributes: PSecurityAttributes;  // 安全屬性,通常 nil
  lInitialCount: Longint;                      // 初始可用信號數 (當前資源數)
  lMaximumCount: Longint;                      // 最大信號數 (資源總上限)
  lpName: PWideChar                            // 具名支援跨進程
): THandle; stdcall;

運作邏輯

  1. 建立時指定 最大數 (如 5 個櫃台) 與 初始數 (如 1 個開放)。
  2. 線程呼叫 WaitForSingleObject(hSem, INFINITE) 申請信號:
    • 信號 > 0:遞減計數,線程繼續執行 (獲得資源)。
    • 信號 = 0:線程阻塞等待 (排隊)。
  3. 線程用畢呼叫 ReleaseSemaphore(hSem, 1, nil) 釋放:
    • 計數遞增 (不超過最大數)。
    • 喚醒一個等待線程 (若有)。
  4. 特殊情況:MaximumCount = 1 時退化為 Mutex。

API 版完整範例:動態初始信號數控制 5 線程並發

unit Unit1;
interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;
type TForm1 = class(TForm)
  Button1: TButton; Edit1: TEdit;
  procedure FormCreate(Sender: TObject);
  procedure FormDestroy(Sender: TObject);
  procedure Button1Click(Sender: TObject);
  procedure Edit1KeyPress(Sender: TObject; var Key: Char);
end;
var Form1: TForm1;
implementation {$R *.dfm}

var f: Integer; hSemaphore: THandle;

function MyThreadFun(p: Pointer): DWORD; stdcall;
var i, y: Integer;
begin
  Inc(f); y := 20 * f;
  if WaitForSingleObject(hSemaphore, INFINITE) = WAIT_OBJECT_0 then
  begin
    for i := 0 to 1000 do
    begin
      Form1.Canvas.Lock; Form1.Canvas.TextOut(20, y, IntToStr(i)); Form1.Canvas.Unlock;
      Sleep(1);
    end;
    ReleaseSemaphore(hSemaphore, 1, nil);  // 歸還信號
  end;
  Result := 0;
end;

procedure TForm1.Button1Click(Sender: TObject);
var ThreadID: DWORD;
begin
  CloseHandle(hSemaphore);
  hSemaphore := CreateSemaphore(nil, StrToInt(Edit1.Text), 5, nil);  // 最大 5,初始由 Edit1 決定
  Repaint; f := 0;
  CreateThread(nil, 0, @MyThreadFun, nil, 0, ThreadID);
  CreateThread(nil, 0, @MyThreadFun, nil, 0, ThreadID);
  CreateThread(nil, 0, @MyThreadFun, nil, 0, ThreadID);
  CreateThread(nil, 0, @MyThreadFun, nil, 0, ThreadID);
  CreateThread(nil, 0, @MyThreadFun, nil, 0, ThreadID);
end;

procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char);
begin
  if not CharInSet(Key, ['1'..'5']) then Key := #0;  // 限制 1-5
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Edit1.Text := '1';
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  CloseHandle(hSemaphore);
end;
end.

TSemaphore 封裝類版本 (SyncObjs 單元)

uses SyncObjs;

var MySemaphore: TSemaphore;

procedure TForm1.Button1Click(Sender: TObject);
var ThreadID: DWORD;
begin
  if Assigned(MySemaphore) then MySemaphore.Free;
  MySemaphore := TSemaphore.Create(nil, StrToInt(Edit1.Text), 5, '');  // 初始、最大、名稱
  Repaint; f := 0;
  CreateThread(nil, 0, @MyThreadFun, nil, 0, ThreadID);
  CreateThread(nil, 0, @MyThreadFun, nil, 0, ThreadID);
  CreateThread(nil, 0, @MyThreadFun, nil, 0, ThreadID);
  CreateThread(nil, 0, @MyThreadFun, nil, 0, ThreadID);
  CreateThread(nil, 0, @MyThreadFun, nil, 0, ThreadID);
end;

function MyThreadFun(p: Pointer): DWORD; stdcall;
var i, y: Integer;
begin
  Inc(f); y := 20 * f;
  if MySemaphore.WaitFor(INFINITE) = wrSignaled then
  begin
    for i := 0 to 1000 do
    begin
      Form1.Canvas.Lock; Form1.Canvas.TextOut(20, y, IntToStr(i)); Form1.Canvas.Unlock;
      Sleep(1);
    end;
    MySemaphore.Release;  // 釋放信號 (預設 +1)
  end;
  Result := 0;
end;

執行效果展示

Edit1=1 時單線程執行;Edit1=3 時三線程並發;Edit1=5 時全並發

DFM 定義

object Form1: TForm1
  object Button1: TButton OnClick = Button1Click
  object Edit1: TEdit OnKeyPress = Edit1KeyPress Text = '1'
end

Semaphore vs Mutex vs CriticalSection

特性SemaphoreMutexCriticalSection
並發資源數可配置 (1..N)固定 1固定 1
跨進程✅ 具名✅ 具名❌
核心物件開銷有有無 (用戶態)
釋放權限任何線程可 Release僅擁有者可 Release任何線程可 Leave
遞歸鎖定不支援✅ 支援✅ 支援

實務應用場景

  1. 連接池管理:資料庫連接池 (最大 20 連線) 用 Semaphore 控制並發借用/歸還。
  2. 頻寬/速率限制:下載線程池,Semaphore 限制同時下載數。
  3. 硬體資源多工:多張掃描卡、串口設備,Semaphore 代表可用設備數。
  4. 生產者/消費者緩衝區:空槽位 Semaphore + 滿項目 Semaphore 雙信號量實現無鎖環狀緩衝。

關鍵 API 細節

  • ReleaseSemaphore(hSem, ReleaseCount, lpPreviousCount):
    • ReleaseCount:通常 1,可一次釋放多個信號。
    • lpPreviousCount:非 nil 時接收釋放**前**的計數值 (調試用)。
  • TSemaphore.Release(Count: Integer = 1):封裝版支援批量釋放。
  • 初始計數可大於最大計數?API 允許但邏輯異常,實際可用上限仍為最大計數。

Semaphore 以其「可配置並發度、跨進程、釋放無擁有權限制」特性,成為資源池、限流器、生產消費者等經典並發模式的原生最佳實現。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved