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

Delphi 多線程程式設計 (19):無同步物件手動協調線程依序執行與 Mutex 同步對比

     編輯:Delphi

探討如何讓多個線程**依順序**執行。方案一:無任何同步物件,利用線程函數結束前啟動下一個線程實現鏈式依序。方案二:使用 Mutex (互斥體) 配合 WaitForSingleObject 同步,但執行順序由系統調度決定,不保證建立順序。

方案一:鏈式啟動 (零同步物件)

核心思想:主線程只啟動第一個線程,每個線程執行完畢前負責建立下一個線程,形成天然的先後順序。

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    PaintBox1: TPaintBox;
    PaintBox2: TPaintBox;
    PaintBox3: TPaintBox;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

const
  colors: array[0..2] of TColor = (clRed, clGreen, clBlue);

var
  hArr: array[0..2] of THandle;
  paintArr: array[0..2] of TPaintBox;

{ 線程函數:參數 n 為索引 0..2 }
function ThreadFun(p: Pointer): Integer; stdcall;
var
  i, n, x1, y1, x2, y2: Integer;
  ThreadID: DWORD;
begin
  n := Integer(p);
  paintArr[n].Color := colors[n];
  
  { 繪製 50 個隨機橢圓 }
  for i := 0 to 50 do
    with paintArr[n] do
    begin
      x1 := Random(Width);  y1 := Random(Height);
      x2 := Random(Width);  y2 := Random(Height);
      Canvas.Lock;
      Canvas.Ellipse(x1, y1, x2, y2);
      Canvas.Unlock;
      Sleep(2);  // 可視化延遲
    end;
  
  { 關鍵:結束前啟動下一個線程 }
  Inc(n);
  if n < 3 then
    hArr[n] := CreateThread(nil, 0, @ThreadFun, Pointer(n), 0, ThreadID);
  
  Result := 0;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  ID: DWORD;
begin
  paintArr[0] := PaintBox1;
  paintArr[1] := PaintBox2;
  paintArr[2] := PaintBox3;
  
  { 只啟動第一個線程 (索引 0) }
  hArr[0] := CreateThread(nil, 0, @ThreadFun, Pointer(0), 0, ID);
end;

procedure TForm1.FormDestroy(Sender: TObject);
var
  i: Integer;
begin
  for i := 0 to Length(hArr) - 1 do
    if hArr[i] <> 0 then CloseHandle(hArr[i]);
end;

end.

方案二:Mutex 同步 (順序不確定)

var
  hMutex: THandle;

function ThreadFun(p: Pointer): Integer; stdcall;
var
  i, n, x1, y1, x2, y2: Integer;
begin
  n := Integer(p);
  paintArr[n].Color := colors[n];
  
  { 等待互斥體,獲得後執行 }
  if WaitForSingleObject(hMutex, INFINITE) = WAIT_OBJECT_0 then
  begin
    for i := 0 to 50 do
      with paintArr[n] do
      begin
        x1 := Random(Width);  y1 := Random(Height);
        x2 := Random(Width);  y2 := Random(Height);
        Canvas.Lock;
        Canvas.Ellipse(x1, y1, x2, y2);
        Canvas.Unlock;
        Sleep(10);
      end;
    ReleaseMutex(hMutex);  // 釋放互斥體
  end;
  Result := 0;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  ID: DWORD;
  i: Integer;
begin
  paintArr[0] := PaintBox1;
  paintArr[1] := PaintBox2;
  paintArr[2] := PaintBox3;
  
  CloseHandle(hMutex);
  hMutex := CreateMutex(nil, False, nil);  // 建立互斥體
  
  { 同時建立 3 個線程,搶佔互斥體 }
  for i := 0 to Length(hArr) - 1 do
    hArr[i] := CreateThread(nil, 0, @ThreadFun, Pointer(i), 0, ID);
end;

執行效果對比

左圖:紅→綠→藍嚴格依序;右圖:三色搶佔順序隨機

特性鏈式啟動Mutex 同步
執行順序**嚴格依索引順序** (0→1→2)**隨機** (搶到鎖者先執行)
同步物件無Mutex
並行度完全串行串行 (但啟動並行)
適用場景嚴格依賴前序結果的流水線互斥存取共享資源,順序不重要
錯誤傳播前序失敗導致後續不執行獨立失敗不影響其他

DFM 視窗定義 (兩例共用)

object Form1: TForm1
  Left = 0
  Top = 0
  Caption = 'Form1'
  ClientHeight = 156
  ClientWidth = 321
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  OnDestroy = FormDestroy
  PixelsPerInch = 96
  TextHeight = 13
  object PaintBox1: TPaintBox
    Left = 8
    Top = 8
    Width = 97
    Height = 113
  end
  object PaintBox2: TPaintBox
    Left = 111
    Top = 8
    Width = 98
    Height = 113
  end
  object PaintBox3: TPaintBox
    Left = 215
    Top = 8
    Width = 98
    Height = 113
  end
  object Button1: TButton
    Left = 238
    Top = 126
    Width = 75
    Height = 25
    Caption = 'Button1'
    TabOrder = 0
    OnClick = Button1Click
  end
end

進階擴展:有序併行模式

若需「階段有序、階段內併行」 (如:階段 1 依序,階段 2 並行) 可組合使用:

var
  PhaseEvent: THandle;  // 手動重置事件

// 階段 1:鏈式依序
// 階段 2:SetEvent(PhaseEvent) 釋放所有等待線程並行

最佳實務建議

  1. 嚴格有序:優先考慮鏈式啟動或 TEvent 手動重置事件陣列 (WaitForMultipleObjects)。
  2. 資源互斥:使用 TMutex / TCriticalSection 保護共享資料,不依賴執行順序。
  3. 避免輪詢:勿用 Sleep + 檢查旗標模擬同步,極度浪費 CPU。
  4. 句柄管理:所有 CreateThread/CreateMutex 必須配對 CloseHandle,建議封裝在 try/finally 或物件解構子中。

「無同步物件實現有序」看似反直覺,實則是利用執行流程本身的時序特性,在特定場景下比重量級同步物件更輕量、更可預測。

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