对线程的使用,是每个开发者都应该熟练掌握的,也是进阶的重要一环。
可以这样说,没有线程,连界面假死的问题都解决不了,就更别谈并行处理来提高效率了。
本例对线程进行改进,打造一个基础的线程,以后线程应用都从此类继承,大大节省了代码,提高了效率。
经长期实践,此代码能够应付许多情况,值得一学。
它的应用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.