多執行緒編程(1) - 先入門
- 2022 年 2 月 24 日
- 筆記
- Delphi 與 多執行緒
先編寫一個循環 50000 次的程式,每次在程式介面左上方(10,10)的位置輸出數字,程式碼如下:
1 procedure TForm1.btn2Click(Sender: TObject); 2 var 3 i: Integer; 4 begin 5 for i := 0 to 500000 do 6 begin 7 Canvas.TextOut(10, 10, IntToStr(i)); 8 end; 9 end;
上面程式運行時, 在程式運行期間拖動窗體,窗體基本是 卡"死" 的。
解決卡死方法1(Application.ProcessMessages)
一個簡單的辦法( Application.ProcessMessages )來解決這個問題,程式碼如下:
1 procedure TForm1.btn2Click(Sender: TObject); 2 var 3 i: Integer; 4 begin 5 for i := 0 to 500000 do 6 begin 7 Canvas.TextOut(10, 10, IntToStr(i)); 8 Application.ProcessMessages; // 解決方法之一 9 end; 10 end;
這個 Application.ProcessMessages 它會檢查並先處理消息隊列中的其他消息.
但這算不上多執行緒, 運行中拖動窗體, 你會發現循環會暫停下來...
在 Delphi 中使用多執行緒有兩種方法:
- 調用 API
- 使用 TThread 類
解決卡死方法2(調用 API)
1 function MyTest(p: Pointer): Integer; stdcall; 2 var 3 i: Integer; 4 begin 5 for i := 0 to 500000 do 6 begin 7 Form1.Canvas.Lock; // 在 Canvas 中 Lock ,其他訪問先暫停 8 Form1.Canvas.TextOut(10, 10, IntToStr(i)); 9 Form1.Canvas.Unlock; // 用完了,解鎖 10 end; 11 Result := 0; 12 end; 13 14 procedure TForm1.btn3Click(Sender: TObject); 15 var 16 ID: Cardinal; 17 begin 18 CreateThread(nil, 0, @MyTest, nil, 0, ID); 19 end;
CreateThread 要使用的函數是系統級的,不能是某個類的方法,必須有嚴格的格式(參數、返回值)要求,還必須用上 stdcall 是協調參數順序的,雖然這裡只有一個參數沒有順序可言,但這是使用系統函數的慣例。
CreateThread 還需要一個 var 參數來接收新建執行緒的 ID,在 Delphi 10.3.3 中為 Cardinal 類型。
解決卡死方法3(使用 TThread 類)
TThread 類有一個抽象方法(Execute),抽象類只能被繼承使用,下面繼承為 TMyThread,主要是實現抽象方法 Execute,等我們實例化 TMyThread 後,首先會執行 Execute 方法中的程式碼。
1 type 2 TMyThread = class(TThread) 3 protected 4 procedure Execute; override; 5 end; 6 7 implementation 8 9 {$R *.dfm} 10 { TMyThread } 11 12 procedure TMyThread.Execute; 13 var 14 i: Integer; 15 begin 16 inherited; 17 FreeOnTerminate := True; 18 for i := 0 to 500000 do 19 begin 20 Form1.Canvas.Lock; 21 Form1.Canvas.TextOut(10, 10, IntToStr(i)); 22 Form1.Canvas.Unlock; 23 end; 24 end; 25 26 procedure TForm1.btn1Click(Sender: TObject); 27 var 28 MyThread: TMyThread; 29 begin 30 MyThread := TMyThread.Create(True); 31 MyThread.Resume; 32 33 //TMyThread.Create(False); 也可以這樣實例化 34 35 //with TMyThread.Create(True) do Resume; 還可以這樣實例化 36 end;
在上面程式碼中,實例化用到的 MyThread 變數,毫無用處,可以直接
TMyThread.Create(False);
執行或者
with TMyThread.Create(True) do Resume;
執行緒建立後不會立即調用 Execute,可以在需要的時候用 Resume 方法執行執行緒。
在 TThread 類的例子中,還有一個這樣的句子:
FreeOnTerminate := True;
由於 TThread 的特殊性,很多時候我們不能確定執行緒什麼時候執行完畢,因此不能 Free,如果 FreeOnTerminate 為 True,執行緒執行完畢後自動釋放。


