delphi 線程教學第二節:在線程時空中操作界面(UI)


第二節:在線程時空中操作界面(UI)
 
1.為什么要用 TThread ?
 
TThread 基於操作系統的線程函數封裝, 隱藏了諸多繁瑣的細節。
適合於大部分情況多線程任務的實現。這個理由足夠了吧?
什么?你要用 windows 的線程 api 來實現多線程?
我可以負責任地告訴你,如果你用 api 來實現多線程任務,
加之你天資聰明,對 delphi 的面向對象思想掌握得非常快,
那么最終也你也會寫一個與 TThread 類似的東西來提高開發效率。
何必折騰呢? 要相信 delphi 的工程師,人家早已看透了一切。咳咳。
同理,要相信微軟的工程師,windows 操作系統是沒有啥大問題的。
更同理,要相信設計手機的工程師,不需要貼膜,人家好不容易把才手機變薄的。
哈哈,扯遠了。。。
(本教程默認操作系統為 windows 7/10 , delphi 的版本為 XE8,大多數代碼均能在 XE2 上運行)
 
2.線程時空中操作界面(UI)到底有什么門道?
 
很多教程中都一再強調,線程時空里,不准直接去更新 UI ,但似乎沒有說明原因。
我們假設UI 界面允許多個線程同時去更新,看看會發生什么情況。
如果兩個線程,同時都在界面相同的區域進行畫圖操作,比如一個要畫綠色,一個要紅色,
那么最終,界面上是不是可能出現一個大花臉?
可以這樣朴實地理解,就知道為什么 UI 不允許多線程去操作了。 不是不能,是不得已。
(線程中不允許直接操作 UI,在安卓下同樣適用)
 
3. TThread.Synchronize() 原理。
 
是用 SendMessage 函數,發了一個 WM_NULL 消息給窗口。
窗口接到消息后再去更新界面。窗口消息響應事件可以理解為主線程時空。
 
以下是接上節的實例,來看如何正確地顯示計算結果在窗口上。
 
unit  Unit10;
interface
uses
   Winapi . Windows, Winapi . Messages, System . SysUtils, System . Variants, System . Classes,  Graphics,
   Vcl . Controls, Vcl . Forms, Vcl . Dialogs, uAccumulation, Vcl . StdCtrls;
type
   TForm10 =  class (TForm)
     Edit1: TEdit;
     Button1: TButton;
     procedure  Button1Click(Sender: TObject);
   private
     procedure  OnAccumulated(Sender: TAccumulationThread);
   end ;
 
implementation
{ $R  *.dfm}
 
procedure  TForm10 . Button1Click(Sender: TObject);
var
   accThread: TAccumulationThread;
begin
   accThread := TAccumulationThread . Create( true );
   accThread . OnAccumulated := self . OnAccumulated;  //指定事件。
   accThread . FreeOnTerminate :=  true // 線程結束后自動釋放
   accThread . Num :=  100 ;
   accThread . Start;
end ;
 
procedure  TForm10 . OnAccumulated(Sender: TAccumulationThread);
begin
   // 這里是線程時空
   // 要更新 UI ,要用 Synchorinize 把更新的操作
   // 塞到主線程時空里去運行。注意理解:“塞!”
   TThread . Synchronize( nil ,
     procedure
     begin
       // 這里的代碼被塞到主線程時空里去了。
       Edit1 . Text := inttostr(Sender . Total);
     end );
   // Synchronize 第一個參數是 nil
   // 第二個參數是一個匿名函數 什么是匿名函數? 以后會介紹到。
end ;
end .
 
unit  uAccumulation;
interface
uses
   Classes;
type
   TAccumulationThread =  class //此為提前申明
   TOnAccumulated =  procedure (Sender: TAccumulationThread)  of  object ;
   // 如果不提前申明,Sender 就要定義成 TObject
   // 在事件函數中,要操作 Sender 就需要強制轉換
   TAccumulationThread =  class (TThread)
   protected
     procedure  Execute; override;
   public
     Num:  integer ;
     Total:  integer ;
     OnAccumulated: TOnAccumulated;
   end ;
 
implementation
 
procedure  TAccumulationThread . Execute;
var
   i:  integer ;
begin
   Total :=  0 ;
   if  Num >  0  then
   begin
     for  i :=  1  to  Num  do
       Total := Total + i
   end ;
   // 當計算完成后,就調用  OnAccumulated 通知調用者
   if  Assigned(OnAccumulated)  then
     OnAccumulated(self);
end ;
end .
 
 4. 哪些代碼運行在線程時空?
 
Execute 函數中運行的、調用的代碼,都是”線程代碼“。與代碼書寫位置無關!!!
Sysnchronize 是個特殊的存在,它可以在線程時空里,把代碼塞到主線程時空里去運行
 
第三節,將實現線程如何保持生命力,創建后可以反復使用。慢慢進入實用階段了,請不要錯過。
 
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM