多執行緒是提升應用程式回應性與並行處理能力的關鍵技術。本文以「三個執行緒分別在三個畫布繪製不同顏色橢圓」為例,展示從獨立函式、參數化單一函式、到陣列化統一管理的三階段重構過程,並說明關鍵同步與資源釋放細節。
最直觀的寫法:每個執行緒對應一個 ThreadFunN,內部硬編碼畫布與顏色。
function ThreadFun1(p: Pointer): Integer; stdcall;
var i, x1,y1,x2,y2: Integer;
begin
Form1.PaintBox1.Canvas.Brush.Color := clRed;
for i := 0 to 50000 do with Form1.PaintBox1 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(0);
end; Result := 0;
end;
{ ThreadFun2/ThreadFun3 同理,分別用 clGreen、clBlue }
啟動:CreateThread(nil, 0, @ThreadFun1, nil, 0, ID) 三次。FormDestroy 呼叫 CloseHandle 釋放句柄。
缺點:程式碼大量重複,維護困難,擴充性差。
利用 CreateThread 的 lpParameter 傳入索引(1/2/3),在單一 ThreadFun 內 case 選擇畫布與顏色。
function ThreadFun(p: Pointer): Integer; stdcall;
var i,x1,y1,x2,y2: Integer; paint: TPaintBox;
begin
case Integer(p) of
1: begin paint := Form1.PaintBox1; paint.Canvas.Brush.Color := clRed; end;
2: begin paint := Form1.PaintBox2; paint.Canvas.Brush.Color := clGreen; end;
3: begin paint := Form1.PaintBox3; paint.Canvas.Brush.Color := clBlue; end;
end;
for i := 0 to 5000 do with paint 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(0);
end; Result := 0;
end;
procedure TForm1.Button1Click(Sender: TObject);
var ID: DWORD;
begin
h1 := CreateThread(nil, 0, @ThreadFun, Ptr(1), 0, ID);
h2 := CreateThread(nil, 0, @ThreadFun, Ptr(2), 0, ID);
h3 := CreateThread(nil, 0, @ThreadFun, Ptr(3), 0, ID);
end;
進步:邏輯集中,減少重複。但仍需手動管理三個句柄變數。
使用常數陣列定義顏色,動態陣列管理畫布與執行緒句柄,迴圈建立/釋放,極易擴充至 N 個執行緒。
const
colors: array[0..2] of TColor = (clRed, clGreen, clBlue);
var
hArr: array[0..2] of THandle;
panitArr: array[0..2] of TPaintBox;
function ThreadFun(p: Pointer): Integer; stdcall;
var i,n,x1,y1,x2,y2: Integer;
begin
n := Integer(p);
panitArr[n].Canvas.Brush.Color := colors[n];
for i := 0 to 5000 do with panitArr[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(0);
end; Result := 0;
end;
procedure TForm1.Button1Click(Sender: TObject);
var ID: DWORD; i: Integer;
begin
panitArr[0] := PaintBox1; panitArr[1] := PaintBox2; panitArr[2] := PaintBox3;
for i := 0 to High(hArr) do
hArr[i] := CreateThread(nil, 0, @ThreadFun, Ptr(i), 0, ID);
end;
procedure TForm1.FormDestroy(Sender: TObject);
var i: Integer;
begin
for i := 0 to High(hArr) do CloseHandle(hArr[i]);
end;
CreateThread 回傳的 THandle 必須在視窗銷毀前呼叫 CloseHandle,否則造成核心物件洩漏。Ptr(i) 將整數轉為無型別指標,執行緒函式以 Integer(p) 取回,類型安全需自行把關。TThread 類別封裝,提供 Synchronize/Queue 安全更新 UI、例外處理、FreeOnTerminate 自動釋放等機制。System.Threading.TTask、TParallel.For(PPL)或執行緒池,避免頻繁建立/銷毀執行緒開銷。Critical Section、Interlocked 或無鎖資料結構保護。從「複製貼上」到「參數化」再到「資料驅動陣列化」,這三階段演進體現了軟體工程「消除重複、提高內聚、便於擴充」的核心思想。