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

多執行緒程式設計實戰:從簡單範例進化到陣列化管理

     編輯:Delphi

多執行緒是提升應用程式回應性與並行處理能力的關鍵技術。本文以「三個執行緒分別在三個畫布繪製不同顏色橢圓」為例,展示從獨立函式、參數化單一函式、到陣列化統一管理的三階段重構過程,並說明關鍵同步與資源釋放細節。

階段一:三個獨立執行緒函式

最直觀的寫法:每個執行緒對應一個 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;

關鍵技術點補充

  • Canvas.Lock/Unlock:VCL Canvas 非執行緒安全,繪圖前必須鎖定,防止多執行緒同時存取導致 GDI 錯誤或畫面撕裂。
  • Sleep(0):主動讓出時間片,降低 CPU 佔用,讓其他執行緒與主執行緒(UI)有機會執行。
  • 句柄管理:CreateThread 回傳的 THandle 必須在視窗銷毀前呼叫 CloseHandle,否則造成核心物件洩漏。
  • 參數傳遞:Ptr(i) 將整數轉為無型別指標,執行緒函式以 Integer(p) 取回,類型安全需自行把關。

進階建議

  1. 實務專案建議使用 TThread 類別封裝,提供 Synchronize/Queue 安全更新 UI、例外處理、FreeOnTerminate 自動釋放等機制。
  2. 大量平行計算可考慮 System.Threading.TTask、TParallel.For(PPL)或執行緒池,避免頻繁建立/銷毀執行緒開銷。
  3. 共享資料需用 Critical Section、Interlocked 或無鎖資料結構保護。

從「複製貼上」到「參數化」再到「資料驅動陣列化」,這三階段演進體現了軟體工程「消除重複、提高內聚、便於擴充」的核心思想。

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