探討如何讓多個線程**依順序**執行。方案一:無任何同步物件,利用線程函數結束前啟動下一個線程實現鏈式依序。方案二:使用 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.
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 |
| 並行度 | 完全串行 | 串行 (但啟動並行) |
| 適用場景 | 嚴格依賴前序結果的流水線 | 互斥存取共享資源,順序不重要 |
| 錯誤傳播 | 前序失敗導致後續不執行 | 獨立失敗不影響其他 |
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) 釋放所有等待線程並行
TEvent 手動重置事件陣列 (WaitForMultipleObjects)。TMutex / TCriticalSection 保護共享資料,不依賴執行順序。Sleep + 檢查旗標模擬同步,極度浪費 CPU。CreateThread/CreateMutex 必須配對 CloseHandle,建議封裝在 try/finally 或物件解構子中。「無同步物件實現有序」看似反直覺,實則是利用執行流程本身的時序特性,在特定場景下比重量級同步物件更輕量、更可預測。