delphi 對TThread擴充TSimpleThread


對線程的使用,是每個開發者都應該熟練掌握的,也是進階的重要一環。

可以這樣說,沒有線程,連界面假死的問題都解決不了,就更別談並行處理來提高效率了。

本例對線程進行改進,打造一個基礎的線程,以后線程應用都從此類繼承,大大節省了代碼,提高了效率。

經長期實踐,此代碼能夠應付許多情況,值得一學。

它的應用1:TReadHtmlThread (讀網頁)

它的應用2: TElegantThread (把多個線程的請求阻塞到另一個線程)

它的應用3: TThreadTimer 多線程 Timer 

  1 unit uSimpleThread;
  2 interface
  3 uses
  4   System.Classes, System.SysUtils, System.SyncObjs;
  5 
  6 type
  7 
  8   // 顯示信息,調用方法 DoOnStatusMsg(AMsg);
  9   TOnStatusMsg = procedure(AMsg: string) of object;
 10 
 11   // 顯示調試信息,一般用於顯示出錯信息,用法 DoOnDebugMsg(AMsg);
 12   TOnDebugMsg = TOnStatusMsg;
 13 
 14   TSimpleThread = class(TThread)
 15   public type // "執行過程"的類別定義
 16 
 17     TGeneralProc = procedure; // 普通的,即 procedure DoSomeThing;
 18     TObjectProc = procedure of object; // 類的,即 TXxxx.DoSomeThign; 用得多
 19     TAnonymousProc = reference to procedure; // 匿名的
 20   private type
 21     TProcKind = (pkGeneral, pkObject, pkAnonymous); // "執行過程"的類別
 22   private
 23 
 24     FGeneralProc: TGeneralProc;
 25     FObjProc: TObjectProc;
 26     FAnoProc: TAnonymousProc;
 27 
 28     FProcKind: TProcKind;
 29 
 30     FEvent: TEvent; // 用於阻塞,它是一個信號量
 31     FActiveX: boolean; // 是否在線程中支持 Com ,如果你要在線程中訪問 IE 的話,就設定為 True
 32 
 33     FOnStatusMsg: TOnStatusMsg;
 34     FOnDebugMsg: TOnDebugMsg;
 35 
 36     FTagID: integer; // 給線程一個代號,在線程池的時候用來作區別
 37     FParam: integer; // 給線程一個參數,方便識別
 38 
 39     procedure SelfStart; // 觸發線程運行
 40 
 41     procedure DoExecute; // 這個函數里面運行的代碼是“線程空間”
 42     procedure DoOnException(e: exception); // 異常信息顯示 調用 DoOnDebugMsg(AMsg);
 43 
 44     procedure SetTagID(const Value: integer);
 45     procedure SetParam(const Value: integer);
 46 
 47     procedure SetOnStatusMsg(const Value: TOnStatusMsg);
 48     procedure SetOnDebugMsg(const Value: TOnDebugMsg);
 49 
 50   protected
 51 
 52     FWaitStop: boolean; // 結束標志,可以在繼承類中使用它,以確定線程是否停止運行
 53 
 54     procedure DoOnStatusMsg(AMsg: string); // 顯示普通信息
 55     procedure DoOnDebugMsg(AMsg: string); // 顯示調式信息
 56 
 57     procedure Execute; override; // 重載 TThread.Execute
 58 
 59     procedure OnThreadProcErr(e: exception); virtual; // 異常發生事件
 60 
 61     procedure WaitThreadStop; // 等待線程結束
 62 
 63     procedure BeforeExecute; virtual; // 看名字,不解釋
 64     Procedure AfterExecute; virtual; // 看名字,不解釋
 65 
 66     procedure SleepExceptStopped(ATimeOut: Cardinal); // 這個高大上了,要解釋一下。
 67     { 有時線程沒有任務時,就會休息一會兒,但是,休息的時候,可能會接收到退出線程的指令
 68       此函數就是在休息的時候也檢查一下停止指令
 69     }
 70 
 71   public
 72 
 73     // 改變一下 Create 的參數,AllowedActiveX:是否允許線程代碼訪問 Com
 74     constructor Create(AllowedActiveX: boolean = false); reintroduce;
 75 
 76     destructor Destroy; override;
 77 
 78     procedure ExeProcInThread(AProc: TGeneralProc); overload; // 這三個,對外的接口。
 79     procedure ExeProcInThread(AProc: TObjectProc); overload;
 80     procedure ExeProcInThread(AProc: TAnonymousProc); overload;
 81 
 82     procedure StartThread; virtual;
 83     { 啟動線程,一般只調用一次。
 84       以后就由線程的響應事件來執行了
 85     }
 86 
 87     procedure StopThread; virtual; // 停止線程
 88 
 89     property OnStatusMsg: TOnStatusMsg read FOnStatusMsg write SetOnStatusMsg;
 90     property OnDebugMsg: TOnDebugMsg read FOnDebugMsg write SetOnDebugMsg;
 91     property WaitStop: boolean read FWaitStop;
 92     property TagID: integer read FTagID write SetTagID;
 93     property Param: integer read FParam write SetParam;
 94 
 95   end;
 96 
 97 implementation
 98 
 99 uses
100   ActiveX;
101 
102 procedure TSimpleThread.AfterExecute;
103 begin
104 end;
105 
106 procedure TSimpleThread.BeforeExecute;
107 begin
108 end;
109 
110 constructor TSimpleThread.Create(AllowedActiveX: boolean);
111 var
112   BGUID: TGUID;
113 begin
114   inherited Create(false);
115   FActiveX := AllowedActiveX;
116   FreeOnTerminate := false; // 我們要手動Free線程
117   CreateGUID(BGUID);
118   FEvent := TEvent.Create(nil, true, false, GUIDToString(BGUID));
119 end;
120 
121 destructor TSimpleThread.Destroy;
122 begin
123   StopThread; // 先停止
124   WaitThreadStop; // 再等待線程停止
125   {
126     在繼承類的 Destroy 中,也要寫上這兩句. 如:
127     暫時未找到更好的辦法,這點代碼省不了
128     destructor TXXThread.Destroy;
129     begin
130     StopThread;
131     WaitThreadStop;
132     xxx.Free;
133     Inherited;
134     end;
135   }
136   FEvent.Free;
137   inherited;
138 end;
139 
140 procedure TSimpleThread.DoExecute; // 此函數內執行的代碼,就是在多線程空間里運行
141 begin
142   BeforeExecute;
143   repeat
144 
145     FEvent.WaitFor;
146     FEvent.ResetEvent; // 下次waitfor 一直等
147     { 這里嘗試了很多些,總 SelfStart 覺得有沖突,經過多次修改並使用證明,
148       沒有必要在這里加鎖,因為只調用 startThread 一次,剩下的交給線程影應事件
149     }
150 
151     if not Terminated then // 如果線程需要退出
152     begin
153 
154       try
155 
156         case FProcKind of
157           pkGeneral: FGeneralProc;
158           pkObject: FObjProc;
159           pkAnonymous: FAnoProc;
160         end;
161 
162       except
163 
164         on e: exception do
165         begin
166           DoOnException(e);
167         end;
168 
169       end;
170 
171     end;
172 
173   until Terminated;
174   AfterExecute;
175   //代碼運行到這里,就表示這個線程不存在了。再也回不去了,必須釋放資源了。
176 end;
177 
178 procedure TSimpleThread.DoOnDebugMsg(AMsg: string);
179 begin
180   if Assigned(FOnDebugMsg) then
181     FOnDebugMsg(AMsg);
182 end;
183 
184 procedure TSimpleThread.DoOnException(e: exception);
185 var
186   sErrMsg: string;
187 begin
188   sErrMsg := 'ClassName:' + ClassName + #13#10;
189   sErrMsg := sErrMsg + 'TagID:' + IntToStr(FTagID) + #13#10;
190   sErrMsg := sErrMsg + 'Param:' + IntToStr(Param) + #13#10;
191   sErrMsg := sErrMsg + 'ErrMsg:' + e.Message + #13#10;
192   DoOnDebugMsg(sErrMsg);
193   OnThreadProcErr(e);
194 end;
195 
196 procedure TSimpleThread.DoOnStatusMsg(AMsg: string);
197 begin
198   if Assigned(FOnStatusMsg) then
199     FOnStatusMsg(AMsg);
200 end;
201 
202 procedure TSimpleThread.Execute;
203 begin
204   //是否支持 Com
205   if FActiveX then
206   begin
207     CoInitialize(nil);
208     try
209       DoExecute;
210     finally
211       CoUninitialize;
212     end;
213   end
214   else
215     DoExecute;
216 end;
217 
218 procedure TSimpleThread.ExeProcInThread(AProc: TGeneralProc);
219 begin
220   FGeneralProc := AProc;
221   FProcKind := pkGeneral;
222   SelfStart;
223 end;
224 
225 procedure TSimpleThread.ExeProcInThread(AProc: TObjectProc);
226 begin
227   FObjProc := AProc;
228   FProcKind := pkObject;
229   SelfStart;
230 end;
231 
232 procedure TSimpleThread.ExeProcInThread(AProc: TAnonymousProc);
233 begin
234   FAnoProc := AProc;
235   FProcKind := pkAnonymous;
236   SelfStart;
237 end;
238 
239 procedure TSimpleThread.OnThreadProcErr(e: exception);
240 begin;
241 end;
242 
243 procedure TSimpleThread.SelfStart;
244 begin
245   //經常多次嘗試,最終寫成這樣,運行沒有問題
246   if FEvent.WaitFor(0) <> wrSignaled then
247     FEvent.SetEvent; // 讓waitfor 不再等
248 end;
249 
250 procedure TSimpleThread.StopThread;
251 begin
252   //繼承類的代碼中,需要檢查 FWaitStop ,來控制線程結束
253   FWaitStop := true;
254 end;
255 
256 procedure TSimpleThread.SetOnDebugMsg(const Value: TOnDebugMsg);
257 begin
258   FOnDebugMsg := Value;
259 end;
260 
261 procedure TSimpleThread.SetOnStatusMsg(const Value: TOnStatusMsg);
262 begin
263   FOnStatusMsg := Value;
264 end;
265 
266 procedure TSimpleThread.SetParam(const Value: integer);
267 begin
268   FParam := Value;
269 end;
270 
271 procedure TSimpleThread.SetTagID(const Value: integer);
272 begin
273   FTagID := Value;
274 end;
275 
276 procedure TSimpleThread.SleepExceptStopped(ATimeOut: Cardinal);
277 var
278   BOldTime: Cardinal;
279 begin
280   // sleep 時檢測退出指令,以確保線程順序退出
281   // 多個線程同時工作,要保證正確退出,確實不容易
282   BOldTime := GetTickCount;
283   while not WaitStop do
284   begin
285     sleep(50);
286     if (GetTickCount - BOldTime) > ATimeOut then
287       break;
288   end;
289 end;
290 
291 procedure TSimpleThread.StartThread;
292 begin
293   FWaitStop := false;
294 end;
295 
296 procedure TSimpleThread.WaitThreadStop;
297 begin
298   //等待線程結束
299   StopThread;
300   Terminate;
301   SelfStart;
302   WaitFor;
303 end;
304 
305 end.
uSimpleThread.pas

附:delphi 進階基礎技能說明


免責聲明!

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



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