Quick.Threads.pas 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687
  1. { ***************************************************************************
  2. Copyright (c) 2016-2018 Kike Pérez
  3. Unit : Quick.Threads
  4. Description : Thread safe collections
  5. Author : Kike Pérez
  6. Version : 1.2
  7. Created : 09/03/2018
  8. Modified : 19/12/2018
  9. This file is part of QuickLib: https://github.com/exilon/QuickLib
  10. ***************************************************************************
  11. Licensed under the Apache License, Version 2.0 (the "License");
  12. you may not use this file except in compliance with the License.
  13. You may obtain a copy of the License at
  14. http://www.apache.org/licenses/LICENSE-2.0
  15. Unless required by applicable law or agreed to in writing, software
  16. distributed under the License is distributed on an "AS IS" BASIS,
  17. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  18. See the License for the specific language governing permissions and
  19. limitations under the License.
  20. *************************************************************************** }
  21. unit Quick.Threads;
  22. {$i QuickLib.inc}
  23. interface
  24. uses
  25. Classes,
  26. Types,
  27. SysUtils,
  28. //Quick.Chrono,
  29. {$IFNDEF FPC}
  30. System.RTLConsts,
  31. System.Generics.Collections,
  32. System.SyncObjs;
  33. {$ELSE}
  34. RtlConsts,
  35. Generics.Collections,
  36. syncobjs;
  37. {$ENDIF}
  38. type
  39. TThreadedQueueCS<T> = class
  40. private
  41. FQueue: array of T;
  42. FQueueSize, FQueueOffset: Integer;
  43. FQueueLock: TCriticalSection;
  44. {$IFDEF FPC}
  45. FQueueCondVar : TEventObject;
  46. {$ELSE}
  47. FQueueCondVar: TConditionVariableCS;
  48. {$ENDIF}
  49. FShutDown: Boolean;
  50. FPushTimeout, FPopTimeout: Cardinal;
  51. FTotalItemsPushed, FTotalItemsPopped: Cardinal;
  52. public
  53. constructor Create(AQueueDepth: Integer = 10; PushTimeout: Cardinal = INFINITE; PopTimeout: Cardinal = INFINITE);
  54. destructor Destroy; override;
  55. procedure Grow(ADelta: Integer);
  56. function PushItem(const AItem: T): TWaitResult; overload;
  57. function PushItem(const AItem: T; var AQueueSize: Integer): TWaitResult; overload;
  58. function PopItem: T; overload;
  59. function PopItem(var AQueueSize: Integer): T; overload;
  60. function PopItem(var AQueueSize: Integer; var AItem: T): TWaitResult; overload;
  61. function PopItem(var AItem: T): TWaitResult; overload;
  62. procedure DoShutDown;
  63. property QueueSize: Integer read FQueueSize;
  64. property ShutDown: Boolean read FShutDown;
  65. property TotalItemsPushed: Cardinal read FTotalItemsPushed;
  66. property TotalItemsPopped: Cardinal read FTotalItemsPopped;
  67. end;
  68. TThreadedQueueList<T> = class
  69. private
  70. fQueue : TQueue<T>;
  71. fQueueSize : Integer;
  72. fQueueLock : TCriticalSection;
  73. {$IFDEF FPC}
  74. FQueueCondVar : TSimpleEvent;
  75. {$ELSE}
  76. FQueueCondVar: TConditionVariableCS;
  77. {$ENDIF}
  78. fShutDown : Boolean;
  79. fPushTimeout : Cardinal;
  80. fPopTimeout : Cardinal;
  81. fTotalItemsPushed : Cardinal;
  82. fTotalItemsPopped : Cardinal;
  83. public
  84. constructor Create(AQueueDepth: Integer = 10; PushTimeout: Cardinal = INFINITE; PopTimeout: Cardinal = INFINITE);
  85. destructor Destroy; override;
  86. procedure Grow(ADelta: Integer);
  87. function PushItem(const AItem: T): TWaitResult; overload;
  88. function PushItem(const AItem: T; var AQueueSize: Integer): TWaitResult; overload;
  89. function PopItem: T; overload;
  90. function PopItem(var AQueueSize: Integer): T; overload;
  91. function PopItem(var AQueueSize: Integer; var AItem: T): TWaitResult; overload;
  92. function PopItem(var AItem: T): TWaitResult; overload;
  93. procedure DoShutDown;
  94. property QueueSize: Integer read FQueueSize;
  95. property ShutDown: Boolean read FShutDown;
  96. property TotalItemsPushed: Cardinal read FTotalItemsPushed;
  97. property TotalItemsPopped: Cardinal read FTotalItemsPopped;
  98. end;
  99. TThreadTask<T> = class(TThread)
  100. private
  101. fMaxQueue : Integer;
  102. fInsertTimeout : Integer;
  103. fExtractTimeout : Integer;
  104. fTaskQueue : TThreadedQueueCS<T>;
  105. function GetTaskQueue : Cardinal;
  106. public
  107. constructor Create;
  108. destructor Destroy; override;
  109. property MaxQueue : Integer read fMaxQueue write fMaxQueue;
  110. property InsertTimeout : Integer read fInsertTimeout write fInsertTimeout;
  111. property ExtractTimeout : Integer read fExtractTimeout write fExtractTimeout;
  112. property TaskQueue : Cardinal read GetTaskQueue;
  113. procedure Execute; override;
  114. function AddTask(Task : T) : Boolean;
  115. procedure Start;
  116. end;
  117. {$IFNDEF FPC}
  118. TThreadObjectList<T: class> = class(TList<T>)
  119. private
  120. fList: TObjectList<T>;
  121. fLock: TObject;
  122. fDuplicates: TDuplicates;
  123. function GetItem(aIndex : Integer) : T;
  124. procedure SetItem(aIndex : Integer; aValue : T);
  125. public
  126. constructor Create(OwnedObjects : Boolean);
  127. destructor Destroy; override;
  128. property Items[Index : Integer] : T read GetItem write SetItem ; default;
  129. procedure Add(const Item: T);
  130. procedure Clear;
  131. function LockList: TObjectList<T>;
  132. procedure Remove(const Item: T); inline;
  133. procedure RemoveItem(const Item: T; Direction: TDirection);
  134. procedure UnlockList; inline;
  135. property Duplicates: TDuplicates read fDuplicates write fDuplicates;
  136. end;
  137. {$ENDIF}
  138. {$IFDEF FPC}
  139. TProc = procedure of object;
  140. {$ENDIF}
  141. IAnonymousThread = interface
  142. procedure Start;
  143. function OnTerminate(aProc : TProc) : IAnonymousThread;
  144. end;
  145. TAnonymousThread = class(TInterfacedObject,IAnonymousThread)
  146. private
  147. fThread : TThread;
  148. fTerminateProc : TProc;
  149. constructor Create(aProc : TProc);
  150. procedure NotifyTerminate(Sender : TObject);
  151. public
  152. class function Execute(aProc : TProc) : IAnonymousThread;
  153. procedure Start;
  154. function OnTerminate(aProc : TProc) : IAnonymousThread;
  155. end;
  156. implementation
  157. { TThreadedQueueCS<T> }
  158. constructor TThreadedQueueCS<T>.Create(AQueueDepth: Integer = 10; PushTimeout: Cardinal = INFINITE; PopTimeout: Cardinal = INFINITE);
  159. begin
  160. inherited Create;
  161. SetLength(FQueue, AQueueDepth);
  162. FQueueLock := TCriticalSection.Create;
  163. {$IFDEF FPC}
  164. FQueueCondVar := TEventObject.Create(nil, True, False, 'TQCS');
  165. {$ELSE}
  166. FQueueCondVar := TConditionVariableCS.Create;
  167. {$ENDIF}
  168. FPushTimeout := PushTimeout;
  169. FPopTimeout := PopTimeout;
  170. end;
  171. destructor TThreadedQueueCS<T>.Destroy;
  172. begin
  173. DoShutDown;
  174. FQueueLock.Free;
  175. FQueueCondVar.Free;
  176. inherited;
  177. end;
  178. procedure TThreadedQueueCS<T>.Grow(ADelta: Integer);
  179. begin
  180. FQueueLock.Enter;
  181. try
  182. SetLength(FQueue, Length(FQueue) + ADelta);
  183. finally
  184. FQueueLock.Leave;
  185. end;
  186. {$IFDEF FPC}
  187. FQueueCondVar.SetEvent;
  188. {$ELSE}
  189. FQueueCondVar.ReleaseAll;
  190. {$ENDIF}
  191. end;
  192. function TThreadedQueueCS<T>.PopItem: T;
  193. var
  194. LQueueSize: Integer;
  195. begin
  196. PopItem(LQueueSize, Result);
  197. end;
  198. function TThreadedQueueCS<T>.PopItem(var AQueueSize: Integer; var AItem: T): TWaitResult;
  199. begin
  200. AItem := Default(T);
  201. FQueueLock.Enter;
  202. try
  203. Result := wrSignaled;
  204. while (Result = wrSignaled) and (FQueueSize = 0) and not FShutDown do
  205. begin
  206. {$IFDEF FPC}
  207. Result := FQueueCondVar.WaitFor(FPopTimeout);
  208. {$ELSE}
  209. Result := FQueueCondVar.WaitFor(FQueueLock, FPopTimeout);
  210. {$ENDIF}
  211. end;
  212. if (FShutDown and (FQueueSize = 0)) or (Result <> wrSignaled) then Exit;
  213. AItem := FQueue[FQueueOffset];
  214. FQueue[FQueueOffset] := Default(T);
  215. if FQueueSize = Length(FQueue) then
  216. begin
  217. {$IFDEF FPC}
  218. FQueueCondVar.SetEvent;
  219. {$ELSE}
  220. FQueueCondVar.ReleaseAll;
  221. {$ENDIF}
  222. end;
  223. Dec(FQueueSize);
  224. Inc(FQueueOffset);
  225. Inc(FTotalItemsPopped);
  226. if FQueueOffset = Length(FQueue) then FQueueOffset := 0;
  227. finally
  228. AQueueSize := FQueueSize;
  229. FQueueLock.Leave;
  230. end;
  231. end;
  232. function TThreadedQueueCS<T>.PopItem(var AItem: T): TWaitResult;
  233. var
  234. LQueueSize: Integer;
  235. begin
  236. Result := PopItem(LQueueSize, AItem);
  237. end;
  238. function TThreadedQueueCS<T>.PopItem(var AQueueSize: Integer): T;
  239. begin
  240. PopItem(AQueueSize, Result);
  241. end;
  242. function TThreadedQueueCS<T>.PushItem(const AItem: T): TWaitResult;
  243. var
  244. LQueueSize: Integer;
  245. begin
  246. Result := PushItem(AItem, LQueueSize);
  247. end;
  248. function TThreadedQueueCS<T>.PushItem(const AItem: T; var AQueueSize: Integer): TWaitResult;
  249. begin
  250. FQueueLock.Enter;
  251. try
  252. Result := wrSignaled;
  253. while (Result = wrSignaled) and (FQueueSize = Length(FQueue)) and not FShutDown do
  254. begin
  255. {$IFDEF FPC}
  256. Result := FQueueCondVar.WaitFor(FPushTimeout);
  257. {$ELSE}
  258. Result := FQueueCondVar.WaitFor(FQueueLock, FPushTimeout);
  259. {$ENDIF}
  260. end;
  261. if FShutDown or (Result <> wrSignaled) then Exit;
  262. if FQueueSize = 0 then
  263. begin
  264. {$IFDEF FPC}
  265. FQueueCondVar.SetEvent;
  266. {$ELSE}
  267. FQueueCondVar.ReleaseAll;
  268. {$ENDIF}
  269. end;
  270. FQueue[(FQueueOffset + FQueueSize) mod Length(FQueue)] := AItem;
  271. Inc(FQueueSize);
  272. Inc(FTotalItemsPushed);
  273. finally
  274. AQueueSize := FQueueSize;
  275. FQueueLock.Leave;
  276. end;
  277. end;
  278. procedure TThreadedQueueCS<T>.DoShutDown;
  279. begin
  280. FShutDown := True;
  281. {$IFDEF FPC}
  282. FQueueCondVar.SetEvent;
  283. {$ELSE}
  284. FQueueCondVar.ReleaseAll;
  285. {$ENDIF}
  286. end;
  287. { TThreadedQueueList<T> }
  288. constructor TThreadedQueueList<T>.Create(AQueueDepth: Integer = 10; PushTimeout: Cardinal = INFINITE; PopTimeout: Cardinal = INFINITE);
  289. begin
  290. inherited Create;
  291. fQueue := TQueue<T>.Create;
  292. fQueue.Capacity := AQueueDepth;
  293. fQueueSize := 0;
  294. fQueueLock := TCriticalSection.Create;
  295. {$IFDEF FPC}
  296. FQueueCondVar := TSimpleEvent.Create; //TEventObject.Create(nil, False, False, 'TQL');
  297. {$ELSE}
  298. fQueueCondVar := TConditionVariableCS.Create;
  299. {$ENDIF}
  300. fPushTimeout := PushTimeout;
  301. fPopTimeout := PopTimeout;
  302. end;
  303. destructor TThreadedQueueList<T>.Destroy;
  304. begin
  305. DoShutDown;
  306. fQueueLock.Free;
  307. fQueueCondVar.Free;
  308. fQueue.Free;
  309. inherited;
  310. end;
  311. procedure TThreadedQueueList<T>.Grow(ADelta: Integer);
  312. begin
  313. fQueueLock.Enter;
  314. try
  315. fQueue.Capacity := fQueue.Capacity + ADelta;
  316. finally
  317. fQueueLock.Leave;
  318. end;
  319. {$IFDEF FPC}
  320. FQueueCondVar.SetEvent;
  321. {$ELSE}
  322. FQueueCondVar.ReleaseAll;
  323. {$ENDIF}
  324. end;
  325. function TThreadedQueueList<T>.PopItem: T;
  326. var
  327. LQueueSize: Integer;
  328. begin
  329. PopItem(LQueueSize, Result);
  330. end;
  331. {$IFDEF FPC}
  332. function TThreadedQueueList<T>.PopItem(var AQueueSize: Integer; var AItem: T): TWaitResult;
  333. //var
  334. //crono : TChronometer;
  335. begin
  336. AItem := Default(T);
  337. //crono := TChronometer.Create(False);
  338. try
  339. Result := wrSignaled;
  340. //writeln('popitem');
  341. //crono.Start;
  342. while (Result = wrSignaled) and (fQueueSize = 0) and not fShutDown do
  343. begin
  344. //crono.Start;
  345. Result := FQueueCondVar.WaitFor(FPopTimeout);
  346. //crono.Stop;
  347. //writeln('in: ' + crono.ElapsedTime);
  348. //if result = twaitresult.wrError then result := twaitresult.wrError;
  349. end;
  350. //crono.Stop;
  351. //writeln('out: ' + crono.ElapsedTime);
  352. fQueueLock.Enter;
  353. try
  354. if (FShutDown and (fQueueSize = 0)) or (Result <> wrSignaled) then Exit;
  355. AItem := fQueue.Extract;
  356. Dec(FQueueSize);
  357. Inc(fTotalItemsPopped);
  358. finally
  359. fQueueLock.Leave;
  360. end;
  361. finally
  362. AQueueSize := fQueueSize;
  363. end;
  364. end;
  365. {$ELSE}
  366. function TThreadedQueueList<T>.PopItem(var AQueueSize: Integer; var AItem: T): TWaitResult;
  367. begin
  368. AItem := Default(T);
  369. fQueueLock.Enter;
  370. try
  371. Result := wrSignaled;
  372. while (Result = wrSignaled) and (fQueueSize = 0) and not fShutDown do
  373. begin
  374. Result := FQueueCondVar.WaitFor(FQueueLock, FPopTimeout);
  375. end;
  376. if (FShutDown and (fQueueSize = 0)) or (Result <> wrSignaled) then Exit;
  377. AItem := fQueue.Extract;
  378. if fQueueSize = fQueue.Count then
  379. begin
  380. FQueueCondVar.ReleaseAll;
  381. end;
  382. Dec(FQueueSize);
  383. Inc(fTotalItemsPopped);
  384. finally
  385. AQueueSize := fQueueSize;
  386. fQueueLock.Leave;
  387. end;
  388. end;
  389. {$ENDIF}
  390. function TThreadedQueueList<T>.PopItem(var AItem: T): TWaitResult;
  391. var
  392. LQueueSize: Integer;
  393. begin
  394. Result := PopItem(LQueueSize, AItem);
  395. end;
  396. function TThreadedQueueList<T>.PopItem(var AQueueSize: Integer): T;
  397. begin
  398. PopItem(AQueueSize, Result);
  399. end;
  400. function TThreadedQueueList<T>.PushItem(const AItem: T): TWaitResult;
  401. var
  402. LQueueSize: Integer;
  403. begin
  404. Result := PushItem(AItem, LQueueSize);
  405. end;
  406. {$IFDEF FPC}
  407. function TThreadedQueueList<T>.PushItem(const AItem: T; var AQueueSize: Integer): TWaitResult;
  408. begin
  409. FQueueLock.Enter;
  410. try
  411. Result := wrSignaled;
  412. //while (Result = wrSignaled) and (fQueueSize = fQueue.Count) and not fShutDown do
  413. //begin
  414. // Result := fQueueCondVar.WaitFor(fQueueLock, fPushTimeout);
  415. //end;
  416. if fShutDown or (Result <> wrSignaled) then Exit;
  417. //if fQueueSize = 0 then
  418. //begin
  419. // FQueueCondVar.SetEvent;
  420. //end;
  421. fQueue.Enqueue(AItem);
  422. Inc(FQueueSize);
  423. Inc(fTotalItemsPushed);
  424. finally
  425. AQueueSize := fQueueSize;
  426. FQueueLock.Leave;
  427. //FQueueCondVar.SetEvent;
  428. end;
  429. end;
  430. {$ELSE}
  431. function TThreadedQueueList<T>.PushItem(const AItem: T; var AQueueSize: Integer): TWaitResult;
  432. begin
  433. FQueueLock.Enter;
  434. try
  435. Result := wrSignaled;
  436. //while (Result = wrSignaled) and (fQueueSize = fQueue.Count) and not fShutDown do
  437. //begin
  438. // Result := fQueueCondVar.WaitFor(fQueueLock, fPushTimeout);
  439. //end;
  440. if fShutDown or (Result <> wrSignaled) then Exit;
  441. if fQueueSize = 0 then FQueueCondVar.ReleaseAll;
  442. fQueue.Enqueue(AItem);
  443. Inc(FQueueSize);
  444. Inc(fTotalItemsPushed);
  445. finally
  446. AQueueSize := fQueueSize;
  447. FQueueLock.Leave;
  448. end;
  449. end;
  450. {$ENDIF}
  451. procedure TThreadedQueueList<T>.DoShutDown;
  452. begin
  453. fShutDown := True;
  454. {$IFDEF FPC}
  455. FQueueCondVar.SetEvent;
  456. {$ELSE}
  457. FQueueCondVar.ReleaseAll;
  458. {$ENDIF}
  459. end;
  460. { TThreadTask<T> }
  461. function TThreadTask<T>.AddTask(Task: T): Boolean;
  462. begin
  463. Result := fTaskQueue.PushItem(Task) = TWaitResult.wrSignaled;
  464. end;
  465. constructor TThreadTask<T>.Create;
  466. begin
  467. inherited Create(True);
  468. fMaxQueue := 10;
  469. fInsertTimeout := INFINITE;
  470. fExtractTimeout := INFINITE;
  471. end;
  472. destructor TThreadTask<T>.Destroy;
  473. begin
  474. if Assigned(fTaskQueue) then fTaskQueue.Free;
  475. inherited;
  476. end;
  477. procedure TThreadTask<T>.Execute;
  478. begin
  479. inherited;
  480. end;
  481. function TThreadTask<T>.GetTaskQueue: Cardinal;
  482. begin
  483. if Assigned(fTaskQueue) then Result := fTaskQueue.QueueSize
  484. else Result := 0;
  485. end;
  486. procedure TThreadTask<T>.Start;
  487. begin
  488. fTaskQueue := TThreadedQueueCS<T>.Create(fMaxQueue,fInsertTimeout,fExtractTimeout);
  489. end;
  490. {$IFNDEF FPC}
  491. { TThreadObjectList<T> }
  492. procedure TThreadObjectList<T>.Add(const Item: T);
  493. begin
  494. LockList;
  495. try
  496. if (Duplicates = dupAccept) or (fList.IndexOf(Item) = -1) then fList.Add(Item)
  497. else if Duplicates = dupError then raise EListError.CreateFmt(SDuplicateItem, [fList.ItemValue(Item)]);
  498. finally
  499. UnlockList;
  500. end;
  501. end;
  502. procedure TThreadObjectList<T>.Clear;
  503. begin
  504. LockList;
  505. try
  506. fList.Clear;
  507. finally
  508. UnlockList;
  509. end;
  510. end;
  511. constructor TThreadObjectList<T>.Create(OwnedObjects : Boolean);
  512. begin
  513. inherited Create;
  514. fLock := TObject.Create;
  515. fList := TObjectList<T>.Create;
  516. fDuplicates := dupIgnore;
  517. end;
  518. destructor TThreadObjectList<T>.Destroy;
  519. begin
  520. LockList;
  521. try
  522. fList.Free;
  523. inherited Destroy;
  524. finally
  525. UnlockList;
  526. fLock.Free;
  527. end;
  528. end;
  529. function TThreadObjectList<T>.GetItem(aIndex: Integer): T;
  530. begin
  531. LockList;
  532. try
  533. Result := fList[aIndex];
  534. finally
  535. UnlockList;
  536. end;
  537. end;
  538. function TThreadObjectList<T>.LockList: TObjectList<T>;
  539. begin
  540. System.TMonitor.Enter(fLock);
  541. Result := fList;
  542. end;
  543. procedure TThreadObjectList<T>.Remove(const Item: T);
  544. begin
  545. RemoveItem(Item, TDirection.FromBeginning);
  546. end;
  547. procedure TThreadObjectList<T>.RemoveItem(const Item: T; Direction: TDirection);
  548. begin
  549. LockList;
  550. try
  551. fList.RemoveItem(Item, Direction);
  552. finally
  553. UnlockList;
  554. end;
  555. end;
  556. procedure TThreadObjectList<T>.SetItem(aIndex: Integer; aValue: T);
  557. begin
  558. LockList;
  559. try
  560. fList[aIndex] := aValue;
  561. finally
  562. UnlockList;
  563. end;
  564. end;
  565. procedure TThreadObjectList<T>.UnlockList;
  566. begin
  567. System.TMonitor.Exit(fLock);
  568. end;
  569. {$ENDIF}
  570. { TThreadEx }
  571. constructor TAnonymousThread.Create(aProc : TProc);
  572. begin
  573. fThread := TThread.CreateAnonymousThread(@aProc);
  574. end;
  575. class function TAnonymousThread.Execute(aProc: TProc): IAnonymousThread;
  576. begin
  577. Result := TAnonymousThread.Create(aProc);
  578. end;
  579. procedure TAnonymousThread.NotifyTerminate(Sender: TObject);
  580. begin
  581. fTerminateProc;
  582. end;
  583. function TAnonymousThread.OnTerminate(aProc: TProc): IAnonymousThread;
  584. begin
  585. Result := Self;
  586. fTerminateProc := aProc;
  587. fThread.OnTerminate := Self.NotifyTerminate;
  588. end;
  589. procedure TAnonymousThread.Start;
  590. begin
  591. fThread.Start;
  592. end;
  593. end.