{"id":132,"date":"2014-08-24T08:21:55","date_gmt":"2014-08-24T00:21:55","guid":{"rendered":"http:\/\/blog.qdac.cc\/?p=132"},"modified":"2014-08-24T08:30:52","modified_gmt":"2014-08-24T00:30:52","slug":"yxdworker-%e5%90%8e%e5%8f%b0%e5%b7%a5%e4%bd%9c%e8%80%85%e7%ae%a1%e7%90%86%e5%ba%93%e7%94%b1qworker%e5%8f%98%e5%bc%82","status":"publish","type":"post","link":"https:\/\/blog.qdac.cc\/?p=132","title":{"rendered":"YxdWorker \u540e\u53f0\u5de5\u4f5c\u8005\u7ba1\u7406\u5e93(\u7531QWorker\u53d8\u5f02)"},"content":{"rendered":"<p>\u8bf4\u660e<br \/>\n&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br \/>\nYxdWorker \u57fa\u4e8eQDAC\u9879\u76ee\u7684QWorker \uff0c\u5e76\u4e14\u7edd\u5927\u90e8\u5206\u4ee3\u7801\u6765\u81ea\u4e8e\u6b64\uff0c<br \/>\n\u611f\u8c22swish\u548c\u4ed6\u7684QWorker\uff0cQDAC\uff0cYxdWorker \u7248\u6743\u5f52swish, YangYxd\u6240\u6709<br \/>\nQWorker\u6765\u81eaQDAC\u9879\u76ee\uff0c\u7248\u6743\u5f52swish(QQ:109867294)\u6240\u6709<br \/>\nQDAC\u5b98\u65b9\u7fa4\uff1a250530692<br \/>\n&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<\/p>\n<p>\u611f\u8c22Swish\u5144\u957f\uff0c\u4e3a\u4e86QWorker\u8d1f\u51fa\u4e86\u5f88\u591a\u65f6\u95f4\u548c\u7cbe\u529b\u3002<\/p>\n<pre class=\"toolbar:2 wrap-toggle:false popup:false lang:delphi decode:true \">{*******************************************************}\r\n{                                                       }\r\n{       YxdWorker \u540e\u53f0\u5de5\u4f5c\u8005\u7ba1\u7406\u5e93                      }\r\n{                                                       }\r\n{       \u7248\u6743\u6240\u6709 (C) 2013 GXT  YangYxd                  }\r\n{                                                       }\r\n{*******************************************************}\r\n{\r\n --------------------------------------------------------------------\r\n  \u8bf4\u660e\r\n --------------------------------------------------------------------\r\n  YxdWorker \u57fa\u4e8eQDAC\u9879\u76ee\u7684QWorker \uff0c\u5e76\u4e14\u7edd\u5927\u90e8\u5206\u4ee3\u7801\u6765\u81ea\u4e8e\u6b64\uff0c\r\n  \u611f\u8c22swish\u548c\u4ed6\u7684QWorker\uff0cQDAC\uff0cYxdWorker \u7248\u6743\u5f52swish, YangYxd\u6240\u6709\r\n  QWorker\u6765\u81eaQDAC\u9879\u76ee\uff0c\u7248\u6743\u5f52swish(QQ:109867294)\u6240\u6709\r\n  QDAC\u5b98\u65b9\u7fa4\uff1a250530692\r\n\r\n --------------------------------------------------------------------\r\n  \u66f4\u65b0\u8bb0\u5f55\r\n --------------------------------------------------------------------\r\n\r\n 2014.08.23 ver 1.0.4\r\n --------------------------------------------------------------------\r\n  - \u89e3\u51b3Busy\u8ba1\u6570\u5668BUG\r\n\r\n 2014.08.22 ver 1.0.3\r\n --------------------------------------------------------------------\r\n  - \u89e3\u51b3JobGroup\u8d85\u65f6\u548cCancel\u7684\u95ee\u9898\uff0c\u89e3\u51b3\u67d0\u4e9b\u539f\u56e0\u5f15\u8d77\u6d4b\u901f\u5f88\u6162\u7684\u95ee\u9898\r\n  - \u63d0\u53d6\u5408\u5e76\u90e8\u5206\u4ee3\u7801\uff0c\u51cf\u5c11\u4f53\u79ef\r\n\r\n 2014.08.16 ver 1.0.2\r\n --------------------------------------------------------------------\r\n  - \u6539\u8fdb\u957f\u65f6\u95f4\u4efb\u52a1\u5904\u7406\u65b9\u5f0f \uff0cTSimpleJobs\u589e\u52a0 FLongFirst\uff0cFLongLast \u4e13\r\n    \u95e8\u5e94\u5bf9\u957f\u65f6\u95f4\u4efb\u52a1\uff0c\u89e3\u51b3\u957f\u65f6\u95f4\u4efb\u52a1\u5bfc\u81f4Clear\u5931\u8d25BUG\r\n  - \u540c\u6b65QWorker\u4fee\u6539TQJobGroup.AfterDone\u6539\u4e3a\u9664\u4e86\u5728\u5b8c\u6210\u65f6\uff0c\u5728\u4e2d\u65ad\u6216\u8d85\u65f6\r\n    \u65f6\u4ecd\u7136\u89e6\u53d1\r\n  - \u540c\u6b65QWorker\u589e\u52a0TQJobGroup.Run\u51fd\u6570\u52a0\u5165\u8d85\u65f6\u8bbe\u7f6e\uff0c\u8d85\u8fc7\u6307\u5b9a\u7684\u65f6\u95f4\u5982\u679c\r\n    \u4ecd\u672a\u6267\u884c\u5b8c\u6210\uff0c\u5219\u4e2d\u6b62\u540e\u7eed\u6267\u884c\r\n  - \u540c\u6b65QWorker\u589e\u52a0TQJobGroup.Cancel\u51fd\u6570\u7528\u4e8e\u53d6\u6d88\u672a\u6267\u884c\u7684\u4f5c\u4e1a\u6267\u884c\r\n\r\n 2014.08.16 ver 1.0.1\r\n --------------------------------------------------------------------\r\n  - \u589e\u52a0 FOnErrorNotify\u901a\u77e5\u4e8b\u4ef6\uff0c\u4ee5\u4fbf\u4f7f\u7528\u8005\u53ef\u4ee5\u8bb0\u5f55\u76f8\u5173\u65e5\u5fd7\r\n  - \u5c06\u539fQWorker\u4e2d\u7684Delay\uff0cAt\uff0cPost\u5408\u5e76\u4e3aPost\u65b9\u6cd5\u3002\r\n  - \u5c06\u539fQWorker\u4e2d\u7684\u65f6\u95f4\u7cbe\u5ea6\u75310.1ms\u8c03\u6574\u4e3a1ms.\r\n  - \u5c06\u539fQWorker\u4e2dTJobHelper\u7684\u529f\u80fd\u76f4\u63a5\u653e\u5165TJob\u4e2d\uff0c\u4ee5\u4fbf\u5728D2007\u4e2d\u8fd8\u80fd\u4fdd\r\n    \u6301\u826f\u597d\u7684\u8bed\u6cd5\u63d0\u793a\r\n  - \u5c06\u539fQWorker\u4e2dWorker\u7c7b\u8bbe\u7f6eFlags\u76f8\u5173\u529f\u80fd\u6539\u4e3aGetValue,SetValue\uff0c\u51cf\u5c0f\r\n    \u5355\u5143\u5927\u5c0f\r\n  - \u5bf9JobGroup\u7684Add\u529f\u80fd\u589e\u52a0\u53c2\u6570AFreeType, \u5e76\u9ed8\u8ba4AInMainThread=False\r\n  - \u63d0\u53d6\u5408\u5e76\u90e8\u5206\u4ee3\u7801\uff0c\u51cf\u5c11\u5355\u5143\u5927\u5c0f\r\n  - \u5220\u9664Job\u4e2d\u7684Owner\u5b57\u6bb5\r\n\r\n --------------------------------------------------------------------\r\n}\r\nunit YxdWorker;\r\n\r\n{.$DEFINE WORKER_SIMPLE_LOCK} \/\/ \u662f\u5426\u4f7f\u7528\u539f\u5b50\u81ea\u65cb\u9501?\r\n\r\ninterface\r\n\r\nuses\r\n  {$IFDEF UNICODE}Generics.Collections, {$ENDIF}\r\n  {$IFDEF NEXTGEN}Fmx.Forms, System.Diagnostics, {$ENDIF}\r\n  {$IFDEF POSIX}Posix.Unistd, Posix.Pthread, {$ENDIF}\r\n  {$IFDEF MSWINDOWS}Windows, Messages, Forms, Activex, {$ENDIF}\r\n  YxdHash, SysUtils, Classes, Types, SyncObjs;\r\n\r\nconst\r\n  JOB_RUN_ONCE = $0001;         \/\/ \u4f5c\u4e1a\u53ea\u8fd0\u884c\u4e00\u6b21\r\n  JOB_IN_MAINTHREAD = $0002;    \/\/ \u4f5c\u4e1a\u53ea\u80fd\u5728\u4e3b\u7ebf\u7a0b\u4e2d\u8fd0\u884c\r\n  JOB_MAX_WORKERS = $0004;      \/\/ \u5c3d\u53ef\u80fd\u591a\u7684\u5f00\u542f\u53ef\u80fd\u7684\u5de5\u4f5c\u8005\u7ebf\u7a0b\u6765\u5904\u7406\u4f5c\u4e1a\uff0c\u6682\u4e0d\u652f\u6301\r\n  JOB_LONGTIME = $0008;         \/\/ \u4f5c\u4e1a\u9700\u8981\u5f88\u957f\u7684\u65f6\u95f4\u624d\u80fd\u5b8c\u6210\uff0c\u4ee5\u4fbf\u8c03\u5ea6\u7a0b\u5e8f\u51cf\u5c11\u5b83\u5bf9\u5176\u5b83\u4f5c\u4e1a\u7684\u5f71\u54cd\r\n  JOB_SIGNAL_WAKEUP = $0010;    \/\/ \u4f5c\u4e1a\u6839\u636e\u4fe1\u53f7\u9700\u8981\u5524\u9192\r\n  JOB_TERMINATED = $0020;       \/\/ \u4f5c\u4e1a\u4e0d\u9700\u8981\u7ee7\u7eed\u8fdb\u884c\uff0c\u53ef\u4ee5\u7ed3\u675f\u4e86\r\n  JOB_FREE_OBJECT = $0040;      \/\/ Data\u5173\u8054\u7684\u662fObject\uff0c\u4f5c\u4e1a\u5b8c\u6210\u6216\u6e05\u7406\u65f6\u91ca\u653e\r\n  JOB_FREE_RECORD = $0080;      \/\/ Data\u5173\u8054\u7684\u662fRecord\uff0c\u4f5c\u4e1a\u5b8c\u6210\u6216\u6e05\u7406\u65f6\u91ca\u653e\r\n  JOB_FREE_INTERFACE = $0100;   \/\/ Data\u5173\u8054\u7684\u662fInterface\uff0c\u4f5c\u4e1a\u5b8c\u6210\u65f6\u8c03\u7528_Release\r\n  JOB_GROUPED = $0200;          \/\/ \u5f53\u524d\u4f5c\u4e1a\u662f\u4f5c\u4e1a\u7ec4\u7684\u4e00\u5458\r\n  JOB_ANONPROC = $0400;         \/\/ \u5f53\u524d\u4f5c\u4e1a\u8fc7\u7a0b\u662f\u533f\u540d\u51fd\u6570\r\n  JOB_DATA_OWNER = JOB_FREE_OBJECT + JOB_FREE_RECORD + JOB_FREE_INTERFACE;\r\n                                \/\/ \u4f5c\u4e1a\u662fData\u6210\u5458\u7684\u6240\u6709\u8005\r\n\r\n  WORKER_ISBUSY = $01;          \/\/ \u5de5\u4f5c\u8005\u5fd9\u788c\r\n  WORKER_PROCESSLONG = $02;     \/\/ \u5f53\u524d\u5904\u7406\u7684\u4e00\u4e2a\u957f\u65f6\u95f4\u4f5c\u4e1a\r\n  WORKER_RESERVED = $04;        \/\/ \u5f53\u524d\u5de5\u4f5c\u8005\u662f\u4e00\u4e2a\u4fdd\u7559\u5de5\u4f5c\u8005\r\n  WORKER_COM_INITED = $08;      \/\/ \u5de5\u4f5c\u8005\u5df2\u521d\u59cb\u5316\u4e3a\u652f\u6301COM\u7684\u72b6\u6001(\u4ec5\u9650Windows)\r\n  WORKER_LOOKUP = $10;          \/\/ \u5de5\u4f5c\u8005\u6b63\u5728\u67e5\u627e\u4f5c\u4e1a\r\n  WORKER_EXECUTING = $20;       \/\/ \u5de5\u4f5c\u8005\u6b63\u5728\u6267\u884c\u4f5c\u4e1a\r\n  WORKER_EXECUTED = $40;        \/\/ \u5de5\u4f5c\u8005\u5df2\u7ecf\u5b8c\u6210\u4f5c\u4e1a\r\n\r\n  WAITJOB_TIMEOUT = 15000;      \/\/ \u5de5\u4f5c\u8005\u7b49\u5f85\u4f5c\u4e1a\u8d85\u65f6\u65f6\u95f4 (15\u79d2)\r\n\r\nconst\r\n  WOSecond = 1000;             \/\/ 1s\r\n  WOMinute = 60000;            \/\/ 60s\/1min\r\n  WOHour = 3600000;            \/\/ 3600s\/60min\/1hour\r\n  WODay = Int64(86400000);     \/\/ 1\u5929\r\n\r\ntype\r\n  \/\/\/ &lt;summary&gt;\u4f5c\u4e1a\u7a7a\u95f2\u539f\u56e0\uff0c\u5185\u90e8\u4f7f\u7528&lt;\/summary&gt;\r\n  TWorkerIdleReason = (\r\n    irTimeout,                  \/\/ \u5de5\u4f5c\u8005\u5df2\u7ecf\u7b49\u5f85\u8d85\u65f6\uff0c\u53ef\u4ee5\u88ab\u91ca\u653e\r\n    irNoJob                     \/\/ \u6ca1\u6709\u9700\u8981\u5904\u7406\u7684\u4f5c\u4e1a\uff0c\u6b64\u65f6\u5de5\u4f5c\u8005\u4f1a\u8fdb\u884cWAITJOB_TIMEOUT\u91ca\u653e\r\n                                \/\/ \u7b49\u5f85\u72b6\u6001\uff0c\u5982\u679c\u5728WAITJOB_TIMEOUT\u5185\u6709\u65b0\u4f5c\u4e1a\u8fdb\u6765\uff0c\u5219\u5de5\u4f5c\u8005\r\n                                \/\/ \u4f1a\u88ab\u5524\u9192\uff0c\u5426\u5219\u8d85\u65f6\u540e\u4f1a\u88ab\u91ca\u653e\r\n  );\r\n\r\ntype\r\n  \/\/\/ &lt;summary&gt;\u4f5c\u4e1a\u7ed3\u675f\u65f6\u5982\u4f55\u5904\u7406Data\u6210\u5458&lt;\/summary&gt;\r\n  TJobDataFreeType = (\r\n    jdfFreeByUser,              \/\/ \u7528\u6237\u7ba1\u7406\u5bf9\u8c61\u7684\u91ca\u653e\r\n    jdfFreeAsObject,            \/\/ \u9644\u52a0\u7684\u662f\u4e00\u4e2aTObject\u7ee7\u627f\u7684\u5bf9\u8c61\uff0c\u4f5c\u4e1a\u5b8c\u6210\u65f6\u4f1a\u8c03\u7528FreeObject\u91ca\u653e\r\n    jdfFreeAsRecord,            \/\/ \u9644\u52a0\u7684\u662f\u4e00\u4e2aRecord\u5bf9\u8c61\uff0c\u4f5c\u4e1a\u5b8c\u6210\u65f6\u4f1a\u8c03\u7528Dispose\u91ca\u653e\r\n    jdfFreeAsInterface          \/\/ \u9644\u52a0\u7684\u662f\u4e00\u4e2a\u63a5\u53e3\u5bf9\u8c61\uff0c\u6dfb\u52a0\u65f6\u4f1a\u589e\u52a0\u8ba1\u6570\uff0c\u4f5c\u4e1a\u5b8c\u6210\u65f6\u4f1a\u51cf\u5c11\u8ba1\u6570\r\n  );\r\n\r\ntype\r\n  TJobBase = class;\r\n  TJobGroup = class;\r\n  TSimpleJobs = class;\r\n  TRepeatJobs = class;\r\n  TYXDWorker = class;\r\n  TYXDWorkers = class;\r\n  {$IFNDEF UNICODE}\r\n  IntPtr = Integer;\r\n  {$ENDIF}\r\n  PJob = ^TJob;\r\n\r\n  \/\/ \u4f5c\u4e1a\u5904\u7406\u56de\u8c03\u51fd\u6570\r\n  TJobProc = procedure(AJob: PJob) of object;\r\n  TJobProcG = procedure(AJob: PJob);\r\n  {$IFDEF UNICODE}\r\n  TJobProcA = reference to procedure(AJob: PJob);\r\n  {$ENDIF}\r\n\r\n  TWorkerWaitParam = record\r\n    WaitType: Byte;\r\n    Data: Pointer;\r\n    case Integer of\r\n      0:\r\n        (Bound: Pointer); \/\/ \u6309\u5bf9\u8c61\u6e05\u9664\r\n      1:\r\n        (WorkerProc: TMethod);\r\n      2:\r\n        (SourceJob: PJob);\r\n      3:\r\n        (Group: Pointer);\r\n  end;\r\n\r\n  \/\/ \u4fe1\u53f7\u7684\u5185\u90e8\u5b9a\u4e49\r\n  PSignal = ^TSignal;\r\n  TSignal = packed record\r\n    Id: Integer;    \/\/ \u4fe1\u53f7\u7684\u7d22\u5f15\r\n    Fired: Integer; \/\/ \u4fe1\u53f7\u5df2\u89e6\u53d1\u6b21\u6570\r\n    Name: string;   \/\/ \u4fe1\u53f7\u7684\u540d\u79f0\r\n    First: PJob;    \/\/ \u9996\u4e2a\u4f5c\u4e1a\r\n  end;\r\n\r\n  TJob = record\r\n  private\r\n    function GetAvgTime: Integer; inline;\r\n    function GetElapseTime: Int64; inline;\r\n    function GetValue(Index: Integer): Boolean; inline;\r\n    procedure SetValue(Index: Integer; const Value: Boolean); inline;\r\n    function GetIsTerminated: Boolean; inline;\r\n    procedure SetIsTerminated(const Value: Boolean); inline;\r\n    procedure AfterRun(AUsedTime: Int64);\r\n    procedure UpdateNextTime;\r\n  public\r\n    procedure Create(AProc: TJobProc);\r\n    \/\/\/ &lt;summary&gt;\u503c\u62f7\u8d1d\u51fd\u6570&lt;\/summary&gt;\r\n    \/\/\/ &lt;remarks&gt;Worker\/Next\/Source\u4e0d\u4f1a\u590d\u5236\u5e76\u4f1a\u88ab\u7f6e\u7a7a\uff0cOwner\u4e0d\u4f1a\u88ab\u590d\u5236&lt;\/remarks&gt;\r\n    procedure Assign(const ASource: PJob);\r\n    \/\/\/ &lt;summary&gt;\u91cd\u7f6e\u5185\u5bb9\uff0c\u4ee5\u4fbf\u4e3a\u4ece\u961f\u5217\u4e2d\u5f39\u51fa\u505a\u51c6\u5907&lt;\/summary&gt;\r\n    procedure Reset; inline;\r\n\r\n    \/\/\/ &lt;summary&gt;\u5e73\u5747\u6bcf\u6b21\u8fd0\u884c\u65f6\u95f4\uff0c\u5355\u4f4d\u4e3a1ms&lt;\/summary&gt;\r\n    property AvgTime: Integer read GetAvgTime;\r\n    \/\/\/ &lt;summmary&gt;\u672c\u6b21\u5df2\u8fd0\u884c\u65f6\u95f4\uff0c\u5355\u4f4d\u4e3a1ms&lt;\/summary&gt;\r\n    property ElapseTime: Int64 read GetElapseTime;\r\n\r\n    \/\/\/ &lt;summary&gt;\u662f\u5426\u53ea\u8fd0\u884c\u4e00\u6b21\uff0c\u6295\u9012\u4f5c\u4e1a\u65f6\u81ea\u52a8\u8bbe\u7f6e&lt;\/summary&gt;\r\n    property Runonce: Boolean index JOB_RUN_ONCE read GetValue;\r\n    \/\/\/ &lt;summary&gt;\u662f\u5426\u8981\u6c42\u5728\u4e3b\u7ebf\u7a0b\u6267\u884c\u4f5c\u4e1a\uff0c\u5b9e\u9645\u6548\u679c\u6bd4Windows\u7684PostMessage\u76f8\u4f3c&lt;\/summary&gt;\r\n    property InMainThread: Boolean index JOB_IN_MAINTHREAD read GetValue;\r\n    \/\/\/ &lt;summary&gt;\u662f\u5426\u662f\u4e00\u4e2a\u8fd0\u884c\u65f6\u95f4\u6bd4\u8f83\u957f\u7684\u4f5c\u4e1a\uff0c\u7528Workers.LongtimeWork\u8bbe\u7f6e&lt;\/summary&gt;\r\n    property IsLongtimeJob: Boolean index JOB_LONGTIME read GetValue;\r\n    \/\/\/ &lt;summary&gt;\u662f\u5426\u662f\u4e00\u4e2a\u4fe1\u53f7\u89e6\u53d1\u7684\u4f5c\u4e1a&lt;\/summary&gt;\r\n    property IsSignalWakeup: Boolean index JOB_SIGNAL_WAKEUP read GetValue;\r\n    \/\/\/ &lt;summary&gt;\u662f\u5426\u662f\u5206\u7ec4\u4f5c\u4e1a\u7684\u6210\u5458&lt;\/summary&gt;\r\n    property IsGrouped: Boolean index JOB_GROUPED read GetValue;\r\n    \/\/\/ &lt;summary&gt;\u662f\u5426\u8981\u6c42\u7ed3\u675f\u5f53\u524d\u4f5c\u4e1a&lt;\/summary&gt;\r\n    property IsTerminated: Boolean read GetIsTerminated write SetIsTerminated;\r\n    \/\/\/ &lt;summary&gt;\u5224\u65ad\u4f5c\u4e1a\u662f\u5426\u62e5\u6709Data\u6570\u636e\u6210\u5458&lt;\/summary&gt;\r\n    property IsDataOwner: Boolean index JOB_DATA_OWNER read GetValue;\r\n\r\n    \/\/\/ &lt;summary&gt;\u5224\u65ad\u4f5c\u4e1a\u7684Data\u6307\u5411\u7684\u662f\u4e00\u4e2a\u5bf9\u8c61\u4e14\u8981\u6c42\u4f5c\u4e1a\u5b8c\u6210\u65f6\u81ea\u52a8\u91ca\u653e&lt;\/summary&gt;\r\n    property IsObjectOwner: Boolean index JOB_FREE_OBJECT read GetValue write SetValue;\r\n    \/\/\/ &lt;summary&gt;\u5224\u65ad\u4f5c\u4e1a\u7684Data\u6307\u5411\u7684\u662f\u4e00\u4e2a\u8bb0\u5f55\u4e14\u8981\u6c42\u4f5c\u4e1a\u5b8c\u6210\u65f6\u81ea\u52a8\u91ca\u653e&lt;\/summary&gt;\r\n    property IsRecordOwner: Boolean index JOB_FREE_RECORD read GetValue write SetValue;\r\n    \/\/\/ &lt;summary&gt;\u5224\u65ad\u4f5c\u4e1a\u7684Data\u6307\u5411\u7684\u662f\u4e00\u4e2a\u63a5\u53e3\u4e14\u8981\u6c42\u4f5c\u4e1a\u5b8c\u6210\u65f6\u81ea\u52a8\u91ca\u653e&lt;\/summary&gt;\r\n    property IsInterfaceOwner: Boolean index JOB_FREE_INTERFACE read GetValue write SetValue;\r\n    \/\/\/ &lt;summary&gt;\u5224\u65ad\u4f5c\u4e1a\u5904\u7406\u8fc7\u7a0b\u662f\u5426\u662f\u4e00\u4e2a\u533f\u540d\u51fd\u6570&lt;\/summary&gt;\r\n    property IsAnonWorkerProc: Boolean index JOB_ANONPROC read GetValue write SetValue;\r\n  public\r\n    FirstTime: Int64;       \/\/ \u4f5c\u4e1a\u7b2c\u4e00\u6b21\u5f00\u59cb\u65f6\u95f4\r\n    StartTime: Int64;       \/\/ \u672c\u6b21\u4f5c\u4e1a\u5f00\u59cb\u65f6\u95f4,8B\r\n    PushTime: Int64;        \/\/ \u5165\u961f\u65f6\u95f4\r\n    PopTime: Int64;         \/\/ \u51fa\u961f\u65f6\u95f4\r\n    NextTime: Int64;        \/\/ \u4e0b\u4e00\u6b21\u8fd0\u884c\u7684\u65f6\u95f4,+8B=16B\r\n    WorkerProc: TJobProc;   \/\/ \u4f5c\u4e1a\u5904\u7406\u51fd\u6570+8\/16B\r\n    {$IFDEF UNICODE}\r\n    WorkerProcA: TJobProcA;\r\n    {$ENDIF}\r\n    Owner: TJobBase;        \/\/ \u4f5c\u4e1a\u6240\u96b6\u5c5e\u7684\u961f\u5217\r\n    Next: PJob;             \/\/ \u4e0b\u4e00\u4e2a\u7ed3\u70b9\r\n    Worker: TYXDWorker;     \/\/ \u5f53\u524d\u4f5c\u4e1a\u5de5\u4f5c\u8005\r\n    Runs: Integer;          \/\/ \u5df2\u7ecf\u8fd0\u884c\u7684\u6b21\u6570+4B\r\n    MinUsedTime: Integer;   \/\/ \u6700\u5c0f\u8fd0\u884c\u65f6\u95f4+4B\r\n    TotalUsedTime: Integer; \/\/ \u8fd0\u884c\u603b\u8ba1\u82b1\u8d39\u7684\u65f6\u95f4\uff0cTotalUsedTime\/Runs\u53ef\u4ee5\u5f97\u51fa\u5e73\u5747\u6267\u884c\u65f6\u95f4+4B\r\n    MaxUsedTime: Integer;   \/\/ \u6700\u5927\u8fd0\u884c\u65f6\u95f4+4B\r\n    Flags: Integer;         \/\/ \u4f5c\u4e1a\u6807\u5fd7\u4f4d+4B\r\n    Data: Pointer;          \/\/ \u9644\u52a0\u6570\u636e\u5185\u5bb9\r\n    case Integer of\r\n      0:\r\n        (\r\n          SignalId: Integer;  \/\/ \u4fe1\u53f7\u7f16\u7801\r\n          Source: PJob;       \/\/ \u6e90\u4f5c\u4e1a\u5730\u5740\r\n          RefCount: PInteger; \/\/ \u6e90\u6570\u636e\r\n        );\r\n      1:\r\n        (\r\n          Interval: Int64;    \/\/ \u8fd0\u884c\u65f6\u95f4\u95f4\u9694\uff0c\u5355\u4f4d\u4e3a0.1ms\uff0c\u5b9e\u9645\u7cbe\u5ea6\u53d7\u4e0d\u540c\u64cd\u4f5c\u7cfb\u7edf\u9650\u5236+8B\r\n          FirstDelay: Int64;  \/\/ \u9996\u6b21\u8fd0\u884c\u5ef6\u8fdf\uff0c\u5355\u4f4d\u4e3a0.1ms\uff0c\u9ed8\u8ba4\u4e3a0\r\n        );\r\n      2:\r\n        (\r\n          Group: Pointer;     \/\/ \u5206\u7ec4\u4f5c\u4e1a\u652f\u6301\r\n        );\r\n  end;\r\n\r\n  \/\/ \u4f5c\u4e1a\u961f\u5217\u5bf9\u8c61\u7684\u57fa\u7c7b\uff0c\u63d0\u4f9b\u57fa\u7840\u7684\u63a5\u53e3\u5c01\u88c5\r\n  TJobBase = class(TObject)\r\n  protected\r\n    FOwner: TYXDWorkers;\r\n    function InternalPush(AJob: PJob): Boolean; virtual; abstract;\r\n    function InternalPop: PJob; virtual; abstract;\r\n    function GetCount: Integer; virtual; abstract;\r\n    function GetEmpty: Boolean;\r\n  public\r\n    constructor Create(AOwner: TYXDWorkers); virtual;\r\n    destructor Destroy; override;\r\n    \/\/ \u6295\u5bc4\u4e00\u4e2a\u4f5c\u4e1a (\u5916\u90e8\u4e0d\u5e94\u5c1d\u8bd5\u76f4\u63a5\u6295\u5bc4\u4efb\u52a1\u5230\u961f\u5217\uff0c\u5176\u7531Workers\u7684\u76f8\u5e94\u51fd\u6570\u5185\u90e8\u8c03\u7528\u3002)\r\n    function Push(AJob: PJob): Boolean; virtual;\r\n    \/\/ \u5f39\u51fa\u4e00\u4e2a\u4f5c\u4e1a\r\n    function Pop: PJob; virtual;\r\n    \/\/ \u7a7a\u6240\u6709\u4f5c\u4e1a\r\n    procedure Clear; overload; virtual;\r\n    \/\/ \u6e05\u7a7a\u6307\u5b9a\u7684\u4f5c\u4e1a\r\n    function Clear(AProc: TJobProc; AData: Pointer; AMaxTimes: Integer): Integer; overload; virtual; abstract;\r\n    \/\/ \u6e05\u7a7a\u4e00\u4e2a\u5bf9\u8c61\u5173\u8054\u7684\u6240\u6709\u4f5c\u4e1a\r\n    function Clear(AObject: Pointer; AMaxTimes: Integer): Integer; overload; virtual; abstract;\r\n    \/\/\/ \u4e0d\u53ef\u9760\u8b66\u544a\uff1aCount\u548cEmpty\u503c\u4ec5\u662f\u4e00\u4e2a\u53c2\u8003\uff0c\u5728\u591a\u7ebf\u7a0b\u73af\u5883\u4e0b\u53ef\u80fd\u5e76\u4e0d\u4fdd\u8bc1\u4e0b\u4e00\u53e5\u4ee3\u7801\u6267\u884c\u65f6\uff0c\u4f1a\u4e00\u81f4\r\n    property Empty: Boolean read GetEmpty; \/\/ \u5f53\u524d\u961f\u5217\u662f\u5426\u4e3a\u7a7a\r\n    property Count: Integer read GetCount; \/\/ \u5f53\u524d\u961f\u5217\u5143\u7d20\u6570\u91cf\r\n  end;\r\n\r\n  {$IFDEF WORKER_SIMPLE_LOCK}\r\n  \/\/ \u4e00\u4e2a\u57fa\u4e8e\u4f4d\u9501\u7684\u7b80\u5355\u9501\u5b9a\u5bf9\u8c61\uff0c\u4f7f\u7528\u539f\u5b50\u51fd\u6570\u7f6e\u4f4d\r\n  TSimpleLock = class\r\n  private\r\n    FFlags: Integer;\r\n  public\r\n    constructor Create;\r\n    procedure Enter; inline;\r\n    procedure Leave; inline;\r\n  end;\r\n  {$ELSE}\r\n  TSimpleLock = TCriticalSection;\r\n  {$ENDIF}\r\n\r\n  \/\/\/ &lt;summary&gt;\r\n  \/\/\/ \u5de5\u4f5c\u8005\u7ebf\u7a0b\u4f7f\u7528\u5355\u5411\u94fe\u8868\u7ba1\u7406\uff0c\u800c\u4e0d\u662f\u8fdb\u884c\u6392\u5e8f\u68c0\u7d22\u662f\u56e0\u4e3a\u5bf9\u4e8e\u5de5\u4f5c\u8005\u6570\u91cf\u6709\u9650\uff0c\u989d\u5916\r\n  \/\/\/ \u7684\u5904\u7406\u53cd\u800c\u4e0d\u4f1a\u76f4\u63a5\u6700\u7b80\u5355\u7684\u5faa\u73af\u76f4\u63a5\u6709\u6548\r\n  \/\/\/ &lt;\/summary&gt;\r\n  TYXDWorker = class(TThread)\r\n  private\r\n    FOwner: TYXDWorkers;\r\n    FEvent: TEvent;\r\n    \/\/FNext: TYXDWorker;\r\n    FFlags: Integer;\r\n    FTimeout: Integer;\r\n    FTerminatingJob: PJob;\r\n    function GetValue(Index: Integer): Boolean; inline;\r\n    procedure SetValue(Index: Integer; const Value: Boolean); inline;\r\n    function GetIsIdle: Boolean; inline;\r\n  protected\r\n    FActiveJob: PJob;\r\n    \/\/ \u4e4b\u6240\u4ee5\u4e0d\u76f4\u63a5\u4f7f\u7528FActiveJob\u7684\u76f8\u5173\u65b9\u6cd5\uff0c\u662f\u56e0\u4e3a\u4fdd\u8bc1\u5916\u90e8\u53ef\u4ee5\u7ebf\u7a0b\u5b89\u5168\u7684\u8bbf\u95ee\u8fd9\u4e24\u4e2a\u6210\u5458\r\n    FActiveJobProc: TJobProc;\r\n    FActiveJobData: Pointer;\r\n    FActiveJobSource: PJob;\r\n    FActiveJobGroup: TJobGroup;\r\n    FActiveJobFlags: Integer;\r\n    procedure Execute; override;\r\n    procedure FireInMainThread;\r\n    procedure DoJob(AJob: PJob);\r\n  public\r\n    constructor Create(AOwner: TYXDWorkers); overload;\r\n    destructor Destroy; override;\r\n    procedure ComNeeded(AInitFlags: Cardinal = 0);\r\n    \/\/ \u5224\u65adCOM\u662f\u5426\u5df2\u7ecf\u521d\u59cb\u5316\u4e3a\u652f\u6301COM\r\n    property ComInitialized: Boolean index WORKER_COM_INITED read GetValue;\r\n    \/\/ \u5224\u65ad\u5f53\u524d\u662f\u5426\u5904\u4e8e\u957f\u65f6\u95f4\u4f5c\u4e1a\u5904\u7406\u8fc7\u7a0b\u4e2d\r\n    property InLongtimeJob: Boolean index WORKER_PROCESSLONG read GetValue;\r\n    \/\/ \u5224\u65ad\u5f53\u524d\u662f\u5426\u7a7a\u95f2\r\n    property IsIdle: Boolean read GetIsIdle;\r\n    \/\/ \u5224\u65ad\u5f53\u524d\u662f\u5426\u5fd9\u788c\r\n    property IsBusy: Boolean index WORKER_ISBUSY read GetValue;\r\n    \/\/ \u5224\u65ad\u5f53\u524d\u5de5\u4f5c\u8005\u662f\u5426\u662f\u5185\u90e8\u4fdd\u7559\u7684\u5de5\u4f5c\u8005\r\n    property IsReserved: Boolean index WORKER_RESERVED read GetValue;\r\n    property IsLookuping: Boolean index WORKER_LOOKUP read GetValue;\r\n    property IsExecuting: Boolean index WORKER_EXECUTING read GetValue;\r\n    property IsExecuted: Boolean index WORKER_EXECUTED read GetValue;\r\n  end;\r\n\r\n  \/\/ \u5de5\u4f5c\u8005\u9519\u8bef\u901a\u77e5\u4e8b\u4ef6\r\n  TWorkerErrorNotify = procedure(AJob: PJob; E: Exception; const ErrSource: string) of object;\r\n  \/\/ \u81ea\u5b9a\u4e49\u6570\u636e\u91ca\u653e\u4e8b\u4ef6\r\n  TCustomFreeDataEvent = procedure(ASender: TYXDWorkers; AFreeType: TJobDataFreeType; var AData: Pointer) of object;\r\n\r\n  \/\/\/ &lt;summary&gt;\r\n  \/\/\/ \u5de5\u4f5c\u8005\u7ba1\u7406\u5bf9\u8c61\uff0c\u7528\u6765\u7ba1\u7406\u5de5\u4f5c\u8005\u548c\u4f5c\u4e1a\r\n  \/\/\/ &lt;\/summary&gt;\r\n  TYXDWorkers = class(TObject)\r\n  private\r\n    FWorkers: array of TYXDWorker;\r\n    FWorkerCount: Integer;\r\n    FDisableCount: Integer;\r\n    FBusyCount: Integer;\r\n    FMinWorkers: Integer;\r\n    FMaxWorkers: Integer;\r\n    FMaxSignalId: Integer;\r\n    FLongTimeWorkers: Integer;    \/\/ \u8bb0\u5f55\u4e0b\u957f\u65f6\u95f4\u4f5c\u4e1a\u4e2d\u7684\u5de5\u4f5c\u8005\uff0c\u8fd9\u79cd\u4efb\u52a1\u957f\u65f6\u95f4\u4e0d\u91ca\u653e\u8d44\u6e90\uff0c\u53ef\u80fd\u4f1a\u9020\u6210\u5176\u5b83\u4efb\u52a1\u65e0\u6cd5\u53ca\u65f6\u54cd\u5e94\r\n    FMaxLongtimeWorkers: Integer; \/\/ \u5141\u8bb8\u6700\u591a\u540c\u65f6\u6267\u884c\u7684\u957f\u65f6\u95f4\u4efb\u52a1\u6570\uff0c\u4e0d\u5141\u8bb8\u8d85\u8fc7MaxWorkers\u7684\u4e00\u534a\r\n    FTerminating: Boolean;\r\n    FCPUNum: Integer;\r\n    FLocker: TCriticalSection;\r\n    FSimpleJobs: TSimpleJobs;\r\n    FRepeatJobs: TRepeatJobs;\r\n    FSignalJobs: TYXDHashTable;\r\n\r\n    FOnErrorNotify: TWorkerErrorNotify;\r\n    FOnCustomFreeData: TCustomFreeDataEvent;\r\n    {$IFDEF MSWINDOWS}\r\n    FMainWorker: HWND;\r\n    procedure DoMainThreadWork(var AMsg: TMessage);\r\n    {$ENDIF}\r\n    function GetEnabled: Boolean;\r\n    function PostWaitJob(AJob: PJob; ASignalId: Integer): Boolean;\r\n    function ClearSignalJobs(ASource: PJob): Integer;\r\n    function ClearJobs(AObject: Pointer; AProc: TJobProc; AData: Pointer; AMaxTimes: Integer): Integer;\r\n    function ClearWaitJobs(ASignalId: Integer; const ASignalName: string): Integer;\r\n    procedure SetEnabled(const Value: Boolean);\r\n    procedure SetMaxLongtimeWorkers(const Value: Integer);\r\n    procedure SetMaxWorkers(const Value: Integer);\r\n    procedure SetMinWorkers(const Value: Integer);\r\n    procedure EnableWorkers;\r\n    procedure DisableWorkers;\r\n    procedure ClearWorkers;\r\n    procedure FreeJob(AJob: PJob);\r\n    procedure FreeJobData(AData: Pointer; AFreeType: TJobDataFreeType);\r\n  protected\r\n    function Popup: PJob;\r\n    function Post(AJob: PJob): Boolean; overload;\r\n    function LookupIdleWorker: Boolean;\r\n    function SignalIdByName(const AName: string): Integer;\r\n    procedure SignalWorkDone(AJob: PJob; AUsedTime: Int64);\r\n    procedure WorkerIdle(AWorker: TYXDWorker; AReason: TWorkerIdleReason);\r\n    procedure WorkerTerminate(AWorker: TObject);\r\n    procedure WaitRunningDone(const AParam: TWorkerWaitParam);\r\n    procedure FireSignalJob(ASignal: PSignal; AData: Pointer; AFreeType: TJobDataFreeType);\r\n    procedure DoJobFree(ATable: TObject; AHash: Cardinal; AData: Pointer);\r\n    procedure DoCustomFreeData(AFreeType: TJobDataFreeType; var AData: Pointer);\r\n  public\r\n    constructor Create(AMinWorkers: Integer = 2); overload;\r\n    destructor Destroy; override;\r\n\r\n    \/\/ \u83b7\u53d6Job\u6c60\u5927\u5c0f\r\n    class function JobPoolCount(): Integer;\r\n    \/\/ \u83b7\u53d6\u5b9e\u4f8b\r\n    class function GetInstance: TYXDWorkers;\r\n\r\n    \/\/ \u6e05\u9664\u6240\u6709\u4f5c\u4e1a\r\n    procedure Clear; overload;\r\n    \/\/\/ &lt;summary&gt;\u6e05\u9664\u4e00\u4e2a\u5bf9\u8c61\u76f8\u5173\u7684\u6240\u6709\u4f5c\u4e1a&lt;\/summary&gt;\r\n    \/\/\/ &lt;param name=\"AObject\"&gt;\u8981\u91ca\u653e\u7684\u4f5c\u4e1a\u5904\u7406\u8fc7\u7a0b\u5173\u8054\u5bf9\u8c61&lt;\/param&gt;\r\n    \/\/\/ &lt;param name=\"AMaxTimes\"&gt;\u6700\u591a\u6e05\u9664\u7684\u6570\u91cf\uff0c\u5982\u679c&lt;0\uff0c\u5219\u5168\u6e05&lt;\/param&gt;\r\n    \/\/\/ &lt;returns&gt;\u8fd4\u56de\u5b9e\u9645\u6e05\u9664\u7684\u4f5c\u4e1a\u6570\u91cf&lt;\/returns&gt;\r\n    \/\/\/ &lt;remarks&gt;\u4e00\u4e2a\u5bf9\u8c61\u5982\u679c\u8ba1\u5212\u4e86\u4f5c\u4e1a\uff0c\u5219\u5728\u81ea\u5df1\u91ca\u653e\u524d\u5e94\u8c03\u7528\u672c\u51fd\u6570\u4ee5\u6e05\u9664\u5173\u8054\u7684\u4f5c\u4e1a\uff0c\r\n    \/\/\/ \u5426\u5219\uff0c\u672a\u5b8c\u6210\u7684\u4f5c\u4e1a\u53ef\u80fd\u4f1a\u89e6\u53d1\u5f02\u5e38\u3002&lt;\/remarks&gt;\r\n    function Clear(AObject: Pointer; AMaxTimes: Integer = -1): Integer; overload;\r\n    \/\/\/ &lt;summary&gt;\u6e05\u9664\u6240\u6709\u6295\u5bc4\u7684\u6307\u5b9a\u8fc7\u7a0b\u4f5c\u4e1a&lt;\/summary&gt;\r\n    \/\/\/ &lt;param name=\"AProc\"&gt;\u8981\u6e05\u9664\u7684\u4f5c\u4e1a\u6267\u884c\u8fc7\u7a0b&lt;\/param&gt;\r\n    \/\/\/ &lt;param name=\"AData\"&gt;\u8981\u6e05\u9664\u7684\u4f5c\u4e1a\u9644\u52a0\u6570\u636e\u6307\u9488\u5730\u5740\uff0c\u5982\u679c\u503c\u4e3aPointer(-1)\uff0c\r\n    \/\/\/ \u5219\u6e05\u9664\u6240\u6709\u7684\u76f8\u5173\u8fc7\u7a0b\uff0c\u5426\u5219\uff0c\u53ea\u6e05\u9664\u9644\u52a0\u6570\u636e\u5730\u5740\u4e00\u81f4\u7684\u8fc7\u7a0b&lt;\/param&gt;\r\n    \/\/\/ &lt;param name=\"AMaxTimes\"&gt;\u6700\u591a\u6e05\u9664\u7684\u6570\u91cf\uff0c\u5982\u679c&lt;0\uff0c\u5219\u5168\u6e05&lt;\/param&gt;\r\n    \/\/\/ &lt;returns&gt;\u8fd4\u56de\u5b9e\u9645\u6e05\u9664\u7684\u4f5c\u4e1a\u6570\u91cf&lt;\/returns&gt;\r\n    function Clear(AProc: TJobProc; AData: Pointer; AMaxTimes: Integer = -1): Integer; overload;\r\n    \/\/\/ &lt;summary&gt;\u6e05\u9664\u6307\u5b9a\u4fe1\u53f7\u5173\u8054\u7684\u6240\u6709\u4f5c\u4e1a&lt;\/summary&gt;\r\n    \/\/\/ &lt;param name=\"ASingalId\"&gt;\u8981\u6e05\u9664\u7684\u4fe1\u53f7\u540d\u79f0&lt;\/param&gt;\r\n    \/\/\/ &lt;returns&gt;\u8fd4\u56de\u5b9e\u9645\u6e05\u9664\u7684\u4f5c\u4e1a\u6570\u91cf&lt;\/returns&gt;\r\n    function Clear(const ASignalName: string): Integer; overload;\r\n    \/\/\/ &lt;summary&gt;\u6e05\u9664\u6307\u5b9a\u4fe1\u53f7\u5173\u8054\u7684\u6240\u6709\u4f5c\u4e1a&lt;\/summary&gt;\r\n    \/\/\/ &lt;param name=\"ASingalId\"&gt;\u8981\u6e05\u9664\u7684\u4fe1\u53f7ID&lt;\/param&gt;\r\n    \/\/\/ &lt;returns&gt;\u8fd4\u56de\u5b9e\u9645\u6e05\u9664\u7684\u4f5c\u4e1a\u6570\u91cf&lt;\/returns&gt;\r\n    function Clear(ASignalId: Integer): Integer; overload;\r\n\r\n    \/\/\/ &lt;summary&gt;\u6295\u5bc4\u4e00\u4e2a\u4f5c\u4e1a&lt;\/summary&gt;\r\n    \/\/\/ &lt;param name=\"AJobProc\"&gt;\u8981\u5b9a\u65f6\u6267\u884c\u7684\u4f5c\u4e1a\u8fc7\u7a0b&lt;\/param&gt;\r\n    \/\/\/ &lt;param name=\"ADelay\"&gt;\u7b2c\u4e00\u6b21\u6267\u884c\u524d\u5ef6\u8fdf\u65f6\u95f4\uff0c\u5c0f\u4e8e\u7b49\u4e8e0\u5219\u7acb\u5373\u6267\u884c&lt;\/param&gt;\r\n    \/\/\/ &lt;param name=\"AInterval\"&gt;\u540e\u7eed\u91cd\u590d\u4f5c\u4e1a\u95f4\u9694\uff0c\u5982\u679c\u5c0f\u4e8e\u7b49\u4e8e0\uff0c\u5219\u4f5c\u4e1a\u53ea\u6267\u884c\u4e00\u6b21&lt;\/param&gt;\r\n    \/\/\/ &lt;param name=\"ARunInMainThread\"&gt;\u662f\u5426\u8981\u6c42\u4f5c\u4e1a\u5728\u4e3b\u7ebf\u7a0b\u4e2d\u6267\u884c&lt;\/param&gt;\r\n    function Post(AJobProc: TJobProc; AData: Pointer; ARunInMainThread: Boolean = False;\r\n      const ADelay: Int64 = 0; const AInterval: Int64 = 0; AFreeType: TJobDataFreeType = jdfFreeByUser): Boolean; overload;\r\n    function Post(AJobProc: TJobProcG; AData: Pointer; ARunInMainThread: Boolean = False;\r\n      const ADelay: Int64 = 0; const AInterval: Int64 = 0; AFreeType: TJobDataFreeType = jdfFreeByUser): Boolean; overload;\r\n    {$IFDEF UNICODE}\r\n    function Post(AJobProc: TJobProcA; AData: Pointer; ARunInMainThread: Boolean = False;\r\n      const ADelay: Int64 = 0; const AInterval: Int64 = 0; AFreeType: TJobDataFreeType = jdfFreeByUser): Boolean; overload;\r\n    {$ENDIF}\r\n\r\n    \/\/\/ &lt;summary&gt;\u6295\u5bc4\u4e00\u4e2a\u5728\u6307\u5b9a\u65f6\u95f4\u624d\u5f00\u59cb\u7684\u91cd\u590d\u4f5c\u4e1a&lt;\/summary&gt;\r\n    \/\/\/ &lt;param name=\"AJobProc\"&gt;\u8981\u5b9a\u65f6\u6267\u884c\u7684\u4f5c\u4e1a\u8fc7\u7a0b&lt;\/param&gt;\r\n    \/\/\/ &lt;param name=\"ATime\"&gt;\u6267\u884c\u65f6\u95f4\uff0c\u53ea\u8981\u65f6\u95f4\u90e8\u5206\uff0c\u65e5\u671f\u5ffd\u7565&lt;\/param&gt;\r\n    \/\/\/ &lt;param name=\"AInterval\"&gt;\u540e\u7eed\u91cd\u590d\u4f5c\u4e1a\u95f4\u9694\uff0c\u5982\u679c\u5c0f\u4e8e\u7b49\u4e8e0\uff0c\u5219\u4f5c\u4e1a\u53ea\u6267\u884c\u4e00\u6b21&lt;\/param&gt;\r\n    \/\/\/ &lt;param name=\"ARunInMainThread\"&gt;\u662f\u5426\u8981\u6c42\u4f5c\u4e1a\u5728\u4e3b\u7ebf\u7a0b\u4e2d\u6267\u884c&lt;\/param&gt;\r\n    function Post(AJobProc: TJobProc; const ATime: TDateTime; const AInterval: Int64;\r\n      AData: Pointer; ARunInMainThread: Boolean = False; AFreeType: TJobDataFreeType = jdfFreeByUser): Boolean; overload;\r\n    function Post(AJobProc: TJobProcG; const ATime: TDateTime; const AInterval: Int64;\r\n      AData: Pointer; ARunInMainThread: Boolean = False; AFreeType: TJobDataFreeType = jdfFreeByUser): Boolean; overload;\r\n    {$IFDEF UNICODE}\r\n    function Post(AJobProc: TJobProcA; const ATime: TDateTime; const AInterval: Int64;\r\n      AData: Pointer; ARunInMainThread: Boolean = False; AFreeType: TJobDataFreeType = jdfFreeByUser): Boolean; overload;\r\n    {$ENDIF}\r\n\r\n    \/\/\/ &lt;summary&gt;\u6295\u5bc4\u4e00\u4e2a\u540e\u53f0\u957f\u65f6\u95f4\u6267\u884c\u7684\u4f5c\u4e1a&lt;\/summary&gt;\r\n    \/\/\/ &lt;param name=\"AJobProc\"&gt;\u8981\u6267\u884c\u7684\u4f5c\u4e1a\u8fc7\u7a0b&lt;\/param&gt;\r\n    \/\/\/ &lt;param name=\"AData\"&gt;\u4f5c\u4e1a\u9644\u52a0\u7684\u7528\u6237\u6570\u636e\u6307\u9488&lt;\/param&gt;\r\n    \/\/\/ &lt;returns&gt;\u6210\u529f\u6295\u5bc4\u8fd4\u56deTrue\uff0c\u5426\u5219\u8fd4\u56deFalse&lt;\/returns&gt;\r\n    \/\/\/ &lt;remarks&gt;\u957f\u65f6\u95f4\u4f5c\u4e1a\u5f3a\u5236\u5728\u540e\u53f0\u7ebf\u7a0b\u4e2d\u6267\u884c\uff0c\u800c\u4e0d\u5141\u8bb8\u6295\u9012\u5230\u4e3b\u7ebf\u7a0b\u4e2d\u6267\u884c&lt;\/remarks&gt;\r\n    function PostLongJob(AJobProc: TJobProc; AData: Pointer;\r\n      AFreeType: TJobDataFreeType = jdfFreeByUser): Boolean; overload;\r\n    function PostLongJob(AJobProc: TJobProcG; AData: Pointer;\r\n      AFreeType: TJobDataFreeType = jdfFreeByUser): Boolean; overload;\r\n    {$IFDEF UNICODE}\r\n    function PostLongJob(AJobProc: TJobProcA; AData: Pointer;\r\n      AFreeType: TJobDataFreeType = jdfFreeByUser): Boolean; overload;\r\n    {$ENDIF}\r\n\r\n    \/\/\/ &lt;summary&gt;\u6295\u5bc4\u4e00\u4e2a\u7b49\u5f85\u4fe1\u53f7\u624d\u5f00\u59cb\u7684\u4f5c\u4e1a&lt;\/summary&gt;\r\n    \/\/\/ &lt;param name=\"AJobProc\"&gt;\u8981\u6267\u884c\u7684\u4f5c\u4e1a\u8fc7\u7a0b&lt;\/param&gt;\r\n    \/\/\/ &lt;param name=\"ASignalId\"&gt;\u7b49\u5f85\u7684\u4fe1\u53f7\u7f16\u7801\uff0c\u8be5\u7f16\u7801\u7531RegisterSignal\u51fd\u6570\u8fd4\u56de&lt;\/param&gt;\r\n    \/\/\/ &lt;param name=\"ARunInMainThread\"&gt;\u4f5c\u4e1a\u8981\u6c42\u5728\u4e3b\u7ebf\u7a0b\u4e2d\u6267\u884c&lt;\/param&gt;\r\n    \/\/\/ &lt;returns&gt;\u6210\u529f\u6295\u5bc4\u8fd4\u56deTrue\uff0c\u5426\u5219\u8fd4\u56deFalse&lt;\/returns&gt;\r\n    function PostWait(AJobProc: TJobProc; ASignalId: Integer;\r\n      ARunInMainThread: Boolean = False): Boolean; overload;\r\n    function PostWait(AJobProc: TJobProcG; ASignalId: Integer;\r\n      ARunInMainThread: Boolean = False): Boolean; overload;\r\n    {$IFDEF UNICODE}\r\n    function PostWait(AJobProc: TJobProcA; ASignalId: Integer;\r\n      ARunInMainThread: Boolean = False): Boolean; overload;\r\n    {$ENDIF}\r\n\r\n    \/\/\/ &lt;summary&gt;\u89e6\u53d1\u4e00\u4e2a\u4fe1\u53f7&lt;\/summary&gt;\r\n    \/\/\/ &lt;param name=\"AId\"&gt;\u4fe1\u53f7\u7f16\u7801\uff0c\u7531RegisterSignal\u8fd4\u56de&lt;\/param&gt;\r\n    \/\/\/ &lt;param name=\"AData\"&gt;\u9644\u52a0\u7ed9\u4f5c\u4e1a\u7684\u7528\u6237\u6570\u636e\u6307\u9488\u5730\u5740&lt;\/param&gt;\r\n    \/\/\/ &lt;remarks&gt;\u89e6\u53d1\u4e00\u4e2a\u4fe1\u53f7\u540e\uff0cWorkers\u4f1a\u89e6\u53d1\u6240\u6709\u5df2\u6ce8\u518c\u7684\u4fe1\u53f7\u5173\u8054\u5904\u7406\u8fc7\u7a0b\u7684\u6267\u884c&lt;\/remarks&gt;\r\n    procedure SendSignal(AId: Integer; AData: Pointer = nil; AFreeType: TJobDataFreeType = jdfFreeByUser); overload;\r\n    \/\/\/ &lt;summary&gt;\u6309\u540d\u79f0\u89e6\u53d1\u4e00\u4e2a\u4fe1\u53f7&lt;\/summary&gt;\r\n    \/\/\/ &lt;param name=\"AName\"&gt;\u4fe1\u53f7\u540d\u79f0&lt;\/param&gt;\r\n    \/\/\/ &lt;param name=\"AData\"&gt;\u9644\u52a0\u7ed9\u4f5c\u4e1a\u7684\u7528\u6237\u6570\u636e\u6307\u9488\u5730\u5740&lt;\/param&gt;\r\n    \/\/\/ &lt;remarks&gt;\u89e6\u53d1\u4e00\u4e2a\u4fe1\u53f7\u540e\uff0cWorkers\u4f1a\u89e6\u53d1\u6240\u6709\u5df2\u6ce8\u518c\u7684\u4fe1\u53f7\u5173\u8054\u5904\u7406\u8fc7\u7a0b\u7684\u6267\u884c&lt;\/remarks&gt;\r\n    procedure SendSignal(const AName: string; AData: Pointer = nil; AFreeType: TJobDataFreeType = jdfFreeByUser); overload;\r\n\r\n    \/\/\/ &lt;summary&gt;\u6ce8\u518c\u4e00\u4e2a\u4fe1\u53f7&lt;\/summary&gt;\r\n    \/\/\/ &lt;param name=\"AName\"&gt;\u4fe1\u53f7\u540d\u79f0&lt;\/param&gt;\r\n    \/\/\/ &lt;remarks&gt;\r\n    \/\/\/ 1.\u91cd\u590d\u6ce8\u518c\u540c\u4e00\u540d\u79f0\u7684\u4fe1\u53f7\u5c06\u8fd4\u56de\u540c\u4e00\u4e2a\u7f16\u7801\r\n    \/\/\/ 2.\u4fe1\u53f7\u4e00\u65e6\u6ce8\u518c\uff0c\u5219\u53ea\u6709\u7a0b\u5e8f\u9000\u51fa\u65f6\u624d\u4f1a\u81ea\u52a8\u91ca\u653e\r\n    \/\/\/ &lt;\/remarks&gt;\r\n    function RegisterSignal(const AName: string): Integer;\r\n\r\n\r\n    \/\/ \u6700\u5927\u5141\u8bb8\u5de5\u4f5c\u8005\u6570\u91cf\uff0c\u4e0d\u80fd\u5c0f\u4e8e2\r\n    property MaxWorkers: Integer read FMaxWorkers write SetMaxWorkers;\r\n    \/\/ \u6700\u5c0f\u5de5\u4f5c\u8005\u6570\u91cf\uff0c\u4e0d\u80fd\u5c0f\u4e8e2\r\n    property MinWorkers: Integer read FMinWorkers write SetMinWorkers;\r\n    \/\/ \u5927\u5141\u8bb8\u7684\u957f\u65f6\u95f4\u4f5c\u4e1a\u5de5\u4f5c\u8005\u6570\u91cf\uff0c\u7b49\u4ef7\u4e8e\u5141\u8bb8\u5f00\u59cb\u7684\u957f\u65f6\u95f4\u4f5c\u4e1a\u6570\u91cf\r\n    property MaxLongtimeWorkers: Integer read FMaxLongtimeWorkers write SetMaxLongtimeWorkers;\r\n    \/\/ \u662f\u5426\u5141\u8bb8\u5f00\u59cb\u4f5c\u4e1a\uff0c\u5982\u679c\u4e3afalse\uff0c\u5219\u6295\u5bc4\u7684\u4f5c\u4e1a\u90fd\u4e0d\u4f1a\u88ab\u6267\u884c\uff0c\u76f4\u5230\u6062\u590d\u4e3aTrue\r\n    \/\/ (Enabled\u4e3aFalse\u65f6\u5df2\u7ecf\u8fd0\u884c\u7684\u4f5c\u4e1a\u5c06\u4ecd\u7136\u8fd0\u884c\uff0c\u5b83\u53ea\u5f71\u54cd\u5c1a\u672a\u6267\u884c\u7684\u4f5c\u6765)\r\n    property Enabled: Boolean read GetEnabled write SetEnabled;\r\n    \/\/ \u662f\u5426\u6b63\u5728\u91ca\u653eTQWorkers\u5bf9\u8c61\u81ea\u8eab\r\n    property Terminating: Boolean read FTerminating;\r\n    \/\/ \u5f53\u524d\u7cfb\u7edfCPU\u6570\u91cf\r\n    property CPUNum: Integer read FCPUNum;\r\n    \/\/ \u7e41\u5fd9\u7684\u5de5\u4f5c\u8005\u6570\u91cf\r\n    property BusyWorkerCount: Integer read FBusyCount;\r\n    \/\/ \u5f53\u524d\u5de5\u4f5c\u8005\u6570\u91cf\r\n    property WorkerCount: Integer read FWorkerCount;\r\n    \/\/ \u5de5\u4f5c\u8005\u9519\u8bef\u56de\u8c03\u901a\u77e5\u4e8b\u4ef6\r\n    property OnErrorNotify: TWorkerErrorNotify read FOnErrorNotify write FOnErrorNotify;\r\n    \/\/ \u7528\u6237\u6307\u5b9a\u7684\u4f5c\u4e1a\u7684Data\u5bf9\u8c61\u91ca\u653e\u65b9\u5f0f\r\n    property OnCustomFreeData: TCustomFreeDataEvent read FOnCustomFreeData write FOnCustomFreeData;\r\n  end;\r\n\r\n  {$IFDEF UNICODE}\r\n  TJobItemList = TList&lt;PJob&gt;;\r\n  {$ELSE}\r\n  TJobItemList = TList;\r\n  {$ENDIF}\r\n\r\n  \/\/\/ &lt;summary&gt;\r\n  \/\/\/ \u4f5c\u4e1a\u7ec4\uff0c\u653e\u5728\u4e00\u8d77\u987a\u5e8f\u6267\u884c\u6216\u4e71\u5e8f\u6267\u884c\uff0c\u53ef\u4ee5\u4f7f\u7528 WaitFor \u7b49\u5f85\u5168\u90e8\u5b8c\u6210\r\n  \/\/\/ &lt;\/summary&gt;\r\n  TJobGroup = class(TObject)\r\n  private\r\n    FOwner: TYXDWorkers;\r\n    FLocker: TSimpleLock;\r\n    FEvent: TEvent;           \/\/ \u4e8b\u4ef6\uff0c\u7528\u4e8e\u7b49\u5f85\u4f5c\u4e1a\u5b8c\u6210\r\n    FCount: Integer;\r\n    FByOrder: Boolean;\r\n    FWaitResult: TWaitResult;\r\n    FAfterDone: TNotifyEvent; \/\/ \u4f5c\u4e1a\u5b8c\u6210\u4e8b\u4ef6\u901a\u77e5\r\n    FTimeoutCheck: Boolean; \/\/ \u662f\u5426\u68c0\u67e5\u4f5c\u4e1a\u8d85\u65f6\r\n    FTag: Pointer;\r\n  protected\r\n    FItems: TJobItemList;     \/\/ \u4f5c\u4e1a\u5217\u8868\r\n    FPrepareCount: Integer;   \/\/ \u51c6\u5907\u8ba1\u6570\r\n    procedure DoJobExecuted(AJob: PJob);\r\n    procedure DoJobsTimeout(AJob: PJob);\r\n    procedure DoAfterDone;\r\n  public\r\n    constructor Create(AByOrder: Boolean = False); overload;\r\n    constructor Create(AOwner: TYXDWorkers; AByOrder: Boolean = False); overload;\r\n    destructor Destroy; override;\r\n    \/\/ \u53d6\u6d88\u672a\u5b8c\u6210\u7684\u4f5c\u4e1a\r\n    procedure Cancel;\r\n    \/\/ \u51c6\u5907\u6dfb\u52a0\u4f5c\u4e1a\uff0c\u5b9e\u9645\u589e\u52a0\u5185\u90e8\u8ba1\u6570\u5668\r\n    procedure Prepare;\r\n    \/\/ \u51cf\u5c11\u5185\u90e8\u8ba1\u6570\u5668\uff0c\u5982\u679c\u8ba1\u6570\u5668\u51cf\u4e3a0\uff0c\u5219\u5f00\u59cb\u5b9e\u9645\u6267\u884c\u4f5c\u4e1a\r\n    procedure Run(ATimeout: Cardinal = INFINITE);\r\n    \/\/ \u6dfb\u52a0\u4e00\u4e2a\u4f5c\u4e1a\u8fc7\u7a0b\uff0c\u5982\u679c\u51c6\u5907\u5185\u90e8\u8ba1\u6570\u5668\u4e3a0\uff0c\u5219\u76f4\u63a5\u6267\u884c\uff0c\u5426\u5219\u53ea\u6dfb\u52a0\u5230\u5217\u8868\r\n    function Add(AProc: TJobProc; AData: Pointer; AInMainThread: Boolean = False;\r\n      AFreeType: TJobDataFreeType = jdfFreeByUser): Boolean;\r\n    \/\/ \u7b49\u5f85\u4f5c\u4e1a\u5b8c\u6210\uff0cATimeout\u4e3a\u6700\u957f\u7b49\u5f85\u65f6\u95f4\r\n    function WaitFor(ATimeout: Cardinal = INFINITE): TWaitResult;\r\n    \/\/ \u7b49\u5f85\u4f5c\u4e1a\u5b8c\u6210\uff0cATimeout\u4e3a\u6700\u957f\u7b49\u5f85\u65f6\u95f4\uff0c\u4e0d\u540c\u7684\u662fMsgWaitFor\u4e0d\u963b\u585e\u6d88\u606f\u5904\u7406\r\n    function MsgWaitFor(ATimeout: Cardinal = INFINITE): TWaitResult;\r\n    \/\/ \u672a\u5b8c\u6210\u7684\u4f5c\u4e1a\u6570\u91cf\r\n    property Count: Integer read FCount;\r\n    \/\/ \u5168\u90e8\u4f5c\u4e1a\u6267\u884c\u5b8c\u6210\u65f6\u89e6\u53d1\u7684\u56de\u8c03\u4e8b\u4ef6\r\n    property AfterDone: TNotifyEvent read FAfterDone write FAfterDone;\r\n    \/\/ \u662f\u5426\u662f\u6309\u987a\u5e8f\u6267\u884c(\u5373\u5fc5\u9700\u7b49\u5f85\u4e0a\u4e00\u4e2a\u4f5c\u4e1a\u5b8c\u6210\u540e\u624d\u6267\u884c\u4e0b\u4e00\u4e2a)\r\n    property ByOrder: Boolean read FByOrder;\r\n    property Tag: Pointer read FTag write FTag;\r\n  end;\r\n\r\n  \/\/\/ &lt;summary&gt;\r\n  \/\/\/ \u7528\u4e8e\u7ba1\u7406\u8ba1\u5212\u578b\u4efb\u52a1\uff0c\u9700\u8981\u5728\u6307\u5b9a\u7684\u65f6\u95f4\u70b9\u89e6\u53d1\r\n  \/\/\/ &lt;\/summary&gt;\r\n  TRepeatJobs = class(TJobBase)\r\n  private\r\n    FLocker: TCriticalSection;\r\n    FFirstFireTime: Int64;\r\n    function ClearJobs(AObject: Pointer; AProc: TJobProc; AData: Pointer; AMaxTimes: Integer): Integer;\r\n    procedure AfterJobRun(AJob: PJob; AUsedTime: Int64);\r\n  protected\r\n    FItems: TRBTree;\r\n    function InternalPush(AJob: PJob): Boolean; override;\r\n    function InternalPop: PJob; override;\r\n    function DoTimeCompare(P1, P2: Pointer): Integer;\r\n    procedure DoJobDelete(ATree: TRBTree; ANode: PRBNode);\r\n    function GetCount: Integer; override;\r\n  public\r\n    constructor Create(AOwner: TYXDWorkers); override;\r\n    destructor Destroy; override;\r\n    procedure Clear; override;\r\n    function Clear(AObject: Pointer; AMaxTimes: Integer): Integer; overload; override;\r\n    function Clear(AProc: TJobProc; AData: Pointer; AMaxTimes: Integer): Integer; overload; override;\r\n  end;\r\n\r\n  \/\/\/ &lt;summary&gt;\r\n  \/\/\/ \u7528\u4e8e\u7ba1\u7406\u7b80\u5355\u7684\u5f02\u6b65\u8c03\u7528\uff0c\u6ca1\u6709\u89e6\u53d1\u65f6\u95f4\u8981\u6c42\u7684\u4f5c\u4e1a\r\n  \/\/\/ &lt;\/summary&gt;\r\n  TSimpleJobs = class(TJobBase)\r\n  private\r\n    FFirst, FLast: PJob;\r\n    FCount: Integer;\r\n    FLocker: TSimpleLock;\r\n    function ClearJobs(AObject: Pointer; AProc: TJobProc; AData: Pointer; AMaxTimes: Integer): Integer;\r\n  protected\r\n    function InternalPush(AJob: PJob): Boolean; override;\r\n    function InternalPop: PJob; override;\r\n    function GetCount: Integer; override;\r\n  public\r\n    constructor Create(AOwner: TYXDWorkers); override;\r\n    destructor Destroy; override;\r\n    procedure Clear; overload; override;\r\n    function Clear(AObject: Pointer; AMaxTimes: Integer): Integer; overload; override;\r\n    function Clear(AProc: TJobProc; AData: Pointer; AMaxTimes: Integer): Integer; overload; override;\r\n  end;\r\n\r\nvar\r\n  Workers: TYXDWorkers = nil;  \/\/ \u9700\u8981\u65f6\u521d\u59cb\u5316\uff0c\u4e5f\u53ef\u4ee5\u81ea\u5df1\u5b9a\u4e49\uff0c\u5141\u8bb8\u591a\u4e2a\r\n\r\n\/\/ \u8fd4\u56de\u503c\u7684\u65f6\u95f4\u7cbe\u5ea6\u4e3a1ms\r\nfunction GetTimestamp: Int64;\r\n\/\/ \u83b7\u53d6CPU\u6570\u91cf\r\nfunction GetCPUCount: Integer;\r\n{$IF RTLVersion&lt;26}\r\n\/\/ \u4e3a\u4e0eD2007\u517c\u5bb9, \u539f\u5b50\u64cd\u4f5c\u51fd\u6570\r\nfunction AtomicCmpExchange(var Target: Integer; Value, Comparand: Integer): Integer; inline;\r\nfunction AtomicExchange(var Target: Integer; Value: Integer): Integer; inline;\r\nfunction AtomicIncrement(var Target: Integer): Integer; inline;\r\nfunction AtomicDecrement(var Target: Integer): Integer; inline;\r\n{$IFEND}\r\n{$IFDEF WORKER_SIMPLE_LOCK}\r\n\/\/ \u539f\u5b50\u64cd\u4f5c\u51fd\u6570\r\nfunction AtomicAnd(var Dest: Integer; const AMask: Integer): Integer; inline;\r\nfunction AtomicOr(var Dest: Integer; const AMask: Integer): Integer; inline;\r\n{$ENDIF}\r\n\/\/ \u5c06\u5168\u5c40\u7684\u4f5c\u4e1a\u5904\u7406\u51fd\u6570\u8f6c\u6362\u4e3aTJobProc\u7c7b\u578b\uff0c\u4ee5\u4fbf\u6b63\u5e38\u8c03\u5ea6\u4f7f\u7528\r\nfunction MakeJobProc(const AProc: TJobProcG): TJobProc; inline;\r\n\/\/ \u8bbe\u7f6e\u7ebf\u7a0b\u8fd0\u884c\u7684CPU\r\nprocedure SetThreadCPU(AHandle: THandle; ACpuNo: Integer); inline;\r\n\r\nimplementation\r\n\r\nresourcestring\r\n  SNotSupportNow = '\u5f53\u524d\u5c1a\u672a\u652f\u6301\u529f\u80fd %s';\r\n  SNotInitWorkers = '\u5f53\u524d\u5c1a\u672a\u521d\u59cb\u5316\u6709\u5de5\u4f5c\u8005\u7ba1\u7406\u5bf9\u8c61 TYXDWorkers';\r\n  STooFewWorkers = '\u6307\u5b9a\u7684\u6700\u5c0f\u5de5\u4f5c\u8005\u6570\u91cf\u592a\u5c11(\u5fc5\u9700\u5927\u4e8e\u7b49\u4e8e1)\u3002';\r\n  STooManyLongtimeWorker = '\u4e0d\u80fd\u5141\u8bb8\u592a\u591a\u957f\u65f6\u95f4\u4f5c\u4e1a\u7ebf\u7a0b(\u6700\u591a\u5141\u8bb8\u5de5\u4f5c\u8005\u4e00\u534a)\u3002';\r\n  SBadWaitDoneParam = '\u672a\u77e5\u7684\u7b49\u5f85\u6b63\u5728\u6267\u884c\u4f5c\u4e1a\u5b8c\u6210\u65b9\u5f0f:%d';\r\n\r\n{$IFNDEF UNICODE}\r\nconst\r\n  wrIOCompletion = TWaitResult(4);\r\n{$ENDIF}\r\n\r\n{$IFDEF MSWINDOWS}\r\ntype\r\n  TGetTickCount64 = function: Int64;\r\n{$ENDIF MSWINDOWS}\r\ntype\r\n  TJobPool = class\r\n  protected\r\n    FFirst: PJob;\r\n    FCount: Integer;\r\n    FSize: Integer;\r\n    FLocker: TSimpleLock;\r\n  public\r\n    constructor Create(AMaxSize: Integer);\r\n    destructor Destroy; override;\r\n    procedure Push(AJob: PJob);\r\n    function Pop: PJob;\r\n    property Count: Integer read FCount;\r\n    property Size: Integer read FSize write FSize;\r\n  end;\r\n\r\nvar\r\n  JobPool: TJobPool;\r\n  _CPUCount: Integer;\r\n  {$IFDEF NEXTGEN}\r\n  _Watch: TStopWatch;\r\n  {$ELSE}\r\n  GetTickCount64: TGetTickCount64;\r\n  _PerfFreq: Int64;\r\n  {$ENDIF}\r\n\r\nfunction GetTimestamp: Int64;\r\nbegin\r\n  {$IFDEF NEXTGEN}\r\n  Result := _Watch.Elapsed.Ticks div 10000;\r\n  {$ELSE}\r\n  if _PerfFreq &gt; 0 then begin\r\n    QueryPerformanceCounter(Result);\r\n    Result := Result * 1000 div _PerfFreq;\r\n  end else if Assigned(GetTickCount64) then\r\n    Result := GetTickCount64\r\n  else\r\n    Result := GetTickCount;\r\n  {$ENDIF}\r\nend;\r\n\r\nfunction GetCPUCount: Integer;\r\n{$IFDEF MSWINDOWS}\r\nvar\r\n  si: SYSTEM_INFO;\r\n{$ENDIF}\r\nbegin\r\n  if _CPUCount = 0 then begin\r\n  {$IFDEF MSWINDOWS}\r\n    GetSystemInfo(si);\r\n    Result := si.dwNumberOfProcessors;\r\n  {$ELSE}\/\/ Linux,MacOS,iOS,Andriod{POSIX}\r\n  {$IFDEF POSIX}\r\n    Result := sysconf(_SC_NPROCESSORS_ONLN);\r\n  {$ELSE}\/\/ \u4e0d\u8ba4\u8bc6\u7684\u64cd\u4f5c\u7cfb\u7edf\uff0cCPU\u6570\u9ed8\u8ba4\u4e3a1\r\n    Result := 1;\r\n  {$ENDIF !POSIX}\r\n  {$ENDIF !MSWINDOWS}\r\n  end else\r\n    Result := _CPUCount;\r\nend;\r\n\r\nfunction MakeJobProc(const AProc: TJobProcG): TJobProc;\r\nbegin\r\n  TMethod(Result).Data := nil;\r\n  TMethod(Result).Code := @AProc;\r\nend;\r\n\r\nfunction SameWorkerProc(const P1, P2: TJobProc): Boolean; inline;\r\nbegin\r\n  Result := (TMethod(P1).Code = TMethod(P2).Code) and\r\n    (TMethod(P1).Data = TMethod(P2).Data);\r\nend;\r\n\r\nprocedure SetThreadCPU(AHandle: THandle; ACpuNo: Integer);\r\nbegin\r\n  {$IFDEF MSWINDOWS}\r\n  SetThreadIdealProcessor(AHandle, ACpuNo);\r\n  {$ELSE}\r\n  \/\/ Linux\/Andriod\/iOS\u6682\u65f6\u5ffd\u7565,XE6\u672a\u5f15\u5165sched_setaffinity\u5b9a\u4e49\r\n  {$ENDIF}\r\nend;\r\n\r\n\/\/ \u517c\u5bb92007\u7248\u7684\u539f\u5b50\u64cd\u4f5c\u63a5\u53e3\r\n{$IF RTLVersion&lt;26}\r\nfunction AtomicCmpExchange(var Target: Integer; Value: Integer; Comparand: Integer): Integer; inline;\r\nbegin\r\n  Result := InterlockedCompareExchange(Target, Value, Comparand);\r\nend;\r\n\r\nfunction AtomicIncrement(var Target: Integer): Integer; inline;\r\nbegin\r\n  Result := InterlockedIncrement(Target);\r\nend;\r\n\r\nfunction AtomicDecrement(var Target: Integer): Integer; inline;\r\nbegin\r\n  Result := InterlockedDecrement(Target);\r\nend;\r\n\r\nfunction AtomicExchange(var Target: Integer; Value: Integer): Integer;\r\nbegin\r\n  Result := InterlockedExchange(Target, Value);\r\nend;\r\n{$IFEND &lt;XE5}\r\n\r\n{$IFDEF WORKER_SIMPLE_LOCK}\r\n\/\/ \u4f4d\u4e0e\uff0c\u8fd4\u56de\u539f\u503c\r\nfunction AtomicAnd(var Dest: Integer; const AMask: Integer): Integer; inline;\r\nvar\r\n  i: Integer;\r\nbegin\r\n  repeat\r\n    Result := Dest;\r\n    i := Result and AMask;\r\n  until AtomicCmpExchange(Dest, i, Result) = Result;\r\nend;\r\n\r\n\/\/ \u4f4d\u6216\uff0c\u8fd4\u56de\u539f\u503c\r\nfunction AtomicOr(var Dest: Integer; const AMask: Integer): Integer; inline;\r\nvar\r\n  i: Integer;\r\nbegin\r\n  repeat\r\n    Result := Dest;\r\n    i := Result or AMask;\r\n  until AtomicCmpExchange(Dest, i, Result) = Result;\r\nend;\r\n{$ENDIF}\r\n\r\n{ TJobPool }\r\n\r\nconstructor TJobPool.Create(AMaxSize: Integer);\r\nbegin\r\n  FCount := 0;\r\n  FSize := AMaxSize;\r\n  FLocker := TSimpleLock.Create;\r\nend;\r\n\r\ndestructor TJobPool.Destroy;\r\nvar\r\n  AJob: PJob;\r\nbegin\r\n  FLocker.Enter;\r\n  try\r\n    while FFirst &lt;&gt; nil do begin\r\n      AJob := FFirst.Next;\r\n      Dispose(FFirst);\r\n      FFirst := AJob;\r\n    end;\r\n  finally\r\n    FLocker.Free;\r\n  end;\r\n  inherited;\r\nend;\r\n\r\nfunction TJobPool.Pop: PJob;\r\nbegin\r\n  FLocker.Enter;\r\n  Result := FFirst;\r\n  if Result &lt;&gt; nil then begin\r\n    FFirst := Result.Next;\r\n    Dec(FCount);\r\n  end;\r\n  FLocker.Leave;\r\n  if Result = nil then\r\n    GetMem(Result, SizeOf(TJob));\r\n  Result.Reset;\r\nend;\r\n\r\nprocedure TJobPool.Push(AJob: PJob);\r\nvar\r\n  ADoFree: Boolean;\r\nbegin\r\n  {$IFDEF UNICODE}\r\n  if AJob.IsAnonWorkerProc then\r\n    AJob.WorkerProcA := nil;\r\n  {$ENDIF}\r\n  FLocker.Enter;\r\n  ADoFree := (FCount = FSize);\r\n  if not ADoFree then begin\r\n    AJob.Next := FFirst;\r\n    FFirst := AJob;\r\n    Inc(FCount);\r\n  end;\r\n  FLocker.Leave;\r\n  if ADoFree then\r\n    FreeMem(AJob);\r\nend;\r\n\r\n{ TJobBase }\r\n\r\nprocedure TJobBase.Clear;\r\nvar\r\n  AItem: PJob;\r\nbegin\r\n  while True do begin\r\n    AItem := Pop;\r\n    if AItem &lt;&gt; nil then\r\n      FOwner.FreeJob(AItem)\r\n    else\r\n      Break;\r\n  end;\r\nend;\r\n\r\nconstructor TJobBase.Create(AOwner: TYXDWorkers);\r\nbegin\r\n  FOwner := AOwner;\r\nend;\r\n\r\ndestructor TJobBase.Destroy;\r\nbegin\r\n  Clear;\r\n  inherited;\r\nend;\r\n\r\nfunction TJobBase.GetEmpty: Boolean;\r\nbegin\r\n  Result := (Count = 0);\r\nend;\r\n\r\nfunction TJobBase.Pop: PJob;\r\nbegin\r\n  Result := InternalPop;\r\nend;\r\n\r\nfunction TJobBase.Push(AJob: PJob): Boolean;\r\nbegin\r\n  AJob.Owner := Self;\r\n  AJob.PushTime := GetTimestamp;\r\n  Result := InternalPush(AJob);\r\n  if not Result then begin\r\n    AJob.Next := nil;\r\n    FOwner.FreeJob(AJob);\r\n  end;\r\nend;\r\n\r\n{ TJob }\r\n\r\nprocedure TJob.AfterRun(AUsedTime: Int64);\r\nbegin\r\n  Inc(Runs);\r\n  if AUsedTime &gt; 0 then begin\r\n    Inc(TotalUsedTime, AUsedTime);\r\n    if MinUsedTime = 0 then\r\n      MinUsedTime := AUsedTime\r\n    else if MinUsedTime &gt; AUsedTime then\r\n      MinUsedTime := AUsedTime;\r\n    if MaxUsedTime = 0 then\r\n      MaxUsedTime := AUsedTime\r\n    else if MaxUsedTime &lt; AUsedTime then\r\n      MaxUsedTime := AUsedTime;\r\n  end;\r\nend;\r\n\r\nprocedure TJob.Assign(const ASource: PJob);\r\nbegin\r\n  Self := ASource^;\r\n  \/\/ \u4e0b\u9762\u4e09\u4e2a\u6210\u5458\u4e0d\u62f7\u8d1d\r\n  Worker := nil;\r\n  Next := nil;\r\n  Source := nil;\r\nend;\r\n\r\nfunction TJob.GetAvgTime: Integer;\r\nbegin\r\n  if Runs &gt; 0 then\r\n    Result := TotalUsedTime div Runs\r\n  else\r\n    Result := 0;\r\nend;\r\n\r\nfunction TJob.GetElapseTime: Int64;\r\nbegin\r\n  Result := GetTimestamp - StartTime;\r\nend;\r\n\r\nfunction TJob.GetIsTerminated: Boolean;\r\nbegin\r\n  if Assigned(Worker) and Assigned(Worker.FOwner) then\r\n    Result := Worker.FOwner.Terminating or Worker.Terminated or\r\n      ((Flags and JOB_TERMINATED) &lt;&gt; 0) or (Worker.FTerminatingJob = @Self)\r\n  else\r\n    Result := (Flags and JOB_TERMINATED) &lt;&gt; 0;\r\nend;\r\n\r\nfunction TJob.GetValue(Index: Integer): Boolean;\r\nbegin\r\n  Result := (Flags and Index) &lt;&gt; 0;\r\nend;\r\n\r\nprocedure TJob.Create(AProc: TJobProc);\r\nbegin\r\n  WorkerProc := AProc;\r\n  SetValue(JOB_RUN_ONCE, True);\r\nend;\r\n\r\nprocedure TJob.Reset;\r\nbegin\r\n  FillChar(Self, SizeOf(TJob), 0);\r\nend;\r\n\r\nprocedure TJob.SetIsTerminated(const Value: Boolean);\r\nbegin\r\n  SetValue(JOB_TERMINATED, Value);\r\nend;\r\n\r\nprocedure TJob.SetValue(Index: Integer; const Value: Boolean);\r\nbegin\r\n  if Value then\r\n    Flags := (Flags or Index)\r\n  else\r\n    Flags := (Flags and (not Index));\r\nend;\r\n\r\nprocedure TJob.UpdateNextTime;\r\nbegin\r\n  if (Runs = 0) and (FirstDelay &lt;&gt; 0) then\r\n    NextTime := PushTime + FirstDelay\r\n  else if Interval &lt;&gt; 0 then begin\r\n    if NextTime = 0 then\r\n      NextTime := GetTimestamp + Interval\r\n    else\r\n      Inc(NextTime, Interval);\r\n  end else\r\n    NextTime := GetTimestamp;\r\nend;\r\n\r\n{ TSimpleLock }\r\n\r\n{$IFDEF WORKER_SIMPLE_LOCK}\r\nconstructor TSimpleLock.Create;\r\nbegin\r\n  inherited;\r\n  FFlags := 0;\r\nend;\r\n\r\nprocedure TSimpleLock.Enter;\r\nbegin\r\n  while (AtomicOr(FFlags, $01) and $01) &lt;&gt; 0 do begin\r\n  {$IFDEF MSWINDOWS}\r\n    SwitchToThread;\r\n  {$ELSE}\r\n    TThread.Yield;\r\n  {$ENDIF}\r\n  end;\r\nend;\r\n\r\nprocedure TSimpleLock.Leave;\r\nbegin\r\n  AtomicAnd(FFlags, Integer($FFFFFFFE));\r\nend;\r\n{$ENDIF}\r\n\r\n{ TSimpleJobs }\r\n\r\nfunction TSimpleJobs.ClearJobs(AObject: Pointer; AProc: TJobProc;\r\n  AData: Pointer; AMaxTimes: Integer): Integer;\r\nvar\r\n  AFirst, AJob, APrior, ANext: PJob;\r\n  ACount: Integer;\r\n  b: Boolean;\r\nbegin\r\n  FLocker.Enter;     \/\/ \u5148\u5c06\u6240\u6709\u7684\u5f02\u6b65\u4f5c\u4e1a\u6e05\u7a7a\uff0c\u4ee5\u9632\u6b62\u88ab\u5f39\u51fa\u6267\u884c\r\n  AJob := FFirst;\r\n  ACount := FCount;\r\n  FFirst := nil;\r\n  FLast := nil;\r\n  FCount := 0;\r\n  FLocker.Leave;\r\n\r\n  Result := 0;\r\n  APrior := nil;\r\n  AFirst := nil;\r\n  while (AJob &lt;&gt; nil) and (AMaxTimes &lt;&gt; 0) do begin\r\n    ANext := AJob.Next;\r\n    if AObject &lt;&gt; nil then\r\n      b := TMethod(AJob.WorkerProc).Data = AObject\r\n    else\r\n      b := SameWorkerProc(AJob.WorkerProc, AProc) and (AJob.Data = AData);\r\n    if b then begin\r\n      if APrior &lt;&gt; nil then\r\n        APrior.Next := ANext;\r\n      FOwner.FreeJob(AJob);\r\n      Dec(AMaxTimes);\r\n      Inc(Result);\r\n      Dec(ACount);\r\n    end else begin\r\n      if AFirst = nil then\r\n        AFirst := AJob;\r\n      APrior := AJob;\r\n    end;\r\n    AJob := ANext;\r\n  end;\r\n  if ACount &gt; 0 then begin\r\n    FLocker.Enter;\r\n    AFirst.Next := FFirst;\r\n    FFirst := AFirst;\r\n    Inc(FCount, ACount);\r\n    if FLast = nil then\r\n      FLast := APrior;\r\n    FLocker.Leave;\r\n  end;\r\nend;\r\n\r\nprocedure TSimpleJobs.Clear;\r\nvar\r\n  AFirst: PJob;\r\nbegin\r\n  FLocker.Enter;\r\n  AFirst := FFirst;\r\n  FFirst := nil;\r\n  FLast := nil;\r\n  FCount := 0;\r\n  FLocker.Leave;\r\n  FOwner.FreeJob(AFirst);\r\nend;\r\n\r\nfunction TSimpleJobs.Clear(AProc: TJobProc; AData: Pointer; AMaxTimes: Integer): Integer;\r\nbegin\r\n  Result := ClearJobs(nil, AProc, AData, AMaxTimes);\r\nend;\r\n\r\nfunction TSimpleJobs.Clear(AObject: Pointer; AMaxTimes: Integer): Integer;\r\nbegin\r\n  Result := ClearJobs(AObject, nil, nil, AMaxTimes);\r\nend;\r\n\r\nconstructor TSimpleJobs.Create(AOwner: TYXDWorkers);\r\nbegin\r\n  inherited Create(AOwner);\r\n  FLocker := TSimpleLock.Create;\r\nend;\r\n\r\ndestructor TSimpleJobs.Destroy;\r\nbegin\r\n  inherited;\r\n  FLocker.Free;\r\nend;\r\n\r\nfunction TSimpleJobs.GetCount: Integer;\r\nbegin\r\n  Result := FCount;\r\nend;\r\n\r\nfunction TSimpleJobs.InternalPop: PJob;\r\nbegin\r\n  FLocker.Enter;\r\n  Result := FFirst;\r\n  if Result &lt;&gt; nil then begin\r\n    FFirst := Result.Next;\r\n    if FFirst = nil then\r\n      FLast := nil;\r\n    Dec(FCount);\r\n  end;\r\n  FLocker.Leave;\r\n  if Result &lt;&gt; nil then begin\r\n    Result.PopTime := GetTimestamp;\r\n    Result.Next := nil;\r\n  end;\r\nend;\r\n\r\nfunction TSimpleJobs.InternalPush(AJob: PJob): Boolean;\r\nbegin\r\n  FLocker.Enter;\r\n  if FLast = nil then\r\n    FFirst := AJob\r\n  else\r\n    FLast.Next := AJob;\r\n  FLast := AJob;\r\n  Inc(FCount);\r\n  FLocker.Leave;\r\n  Result := true;\r\nend;\r\n\r\n{ TRepeatJobs }\r\n\r\nprocedure TRepeatJobs.AfterJobRun(AJob: PJob; AUsedTime: Int64);\r\nvar\r\n  ANode: PRBNode;\r\n\r\n  function UpdateSource: Boolean;\r\n  var\r\n    ATemp, APrior: PJob;\r\n  begin\r\n    Result := False;\r\n    ATemp := ANode.Data;\r\n    APrior := nil;\r\n    while ATemp &lt;&gt; nil do begin\r\n      if ATemp = AJob.Source then begin\r\n        if AJob.IsTerminated then begin\r\n          if APrior &lt;&gt; nil then\r\n            APrior.Next := ATemp.Next\r\n          else\r\n            ANode.Data := ATemp.Next;\r\n          ATemp.Next := nil;\r\n          FOwner.FreeJob(ATemp);\r\n          if ANode.Data = nil then\r\n            FItems.Delete(ANode);\r\n        end else\r\n          ATemp.AfterRun(AUsedTime);\r\n        Result := True;\r\n        Break;\r\n      end;\r\n      APrior := ATemp;\r\n      ATemp := ATemp.Next;\r\n    end;\r\n  end;\r\n\r\nbegin\r\n  FLocker.Enter;\r\n  try\r\n    ANode := FItems.Find(AJob);\r\n    if ANode &lt;&gt; nil then begin\r\n      if UpdateSource then\r\n        Exit;\r\n    end;\r\n    ANode := FItems.First;\r\n    while ANode &lt;&gt; nil do begin\r\n      if UpdateSource then\r\n        Break;\r\n      ANode := ANode.Next;\r\n    end;\r\n  finally\r\n    FLocker.Leave;\r\n  end;\r\nend;\r\n\r\nfunction TRepeatJobs.ClearJobs(AObject: Pointer; AProc: TJobProc;\r\n  AData: Pointer; AMaxTimes: Integer): Integer;\r\nvar\r\n  ANode, ANext: PRBNode;\r\n  APriorJob, AJob, ANextJob: PJob;\r\n  ACanDelete, B: Boolean;\r\nbegin\r\n  Result := 0;   \/\/ \u73b0\u5728\u6e05\u7a7a\u91cd\u590d\u7684\u8ba1\u5212\u4f5c\u4e1a\r\n  FLocker.Enter;\r\n  try\r\n    ANode := FItems.First;\r\n    while (ANode &lt;&gt; nil) and (AMaxTimes &lt;&gt; 0) do begin\r\n      ANext := ANode.Next;\r\n      AJob := ANode.Data;\r\n      ACanDelete := True;\r\n      APriorJob := nil;\r\n      while AJob &lt;&gt; nil do begin\r\n        ANextJob := AJob.Next;\r\n        if AObject &lt;&gt; nil then\r\n          B := TMethod(AJob.WorkerProc).Data = AObject\r\n        else\r\n          B := SameWorkerProc(AJob.WorkerProc, AProc) and ((AData = Pointer(-1)) or (AData = AJob.Data));\r\n        if B then begin\r\n          if ANode.Data = AJob then\r\n            ANode.Data := AJob.Next;\r\n          if Assigned(APriorJob) then\r\n            APriorJob.Next := AJob.Next;\r\n          AJob.Next := nil;\r\n          FOwner.FreeJob(AJob);\r\n          Dec(AMaxTimes);\r\n          Inc(Result);\r\n        end else begin\r\n          ACanDelete := False;\r\n          APriorJob := AJob;\r\n        end;\r\n        AJob := ANextJob;\r\n      end;\r\n      if ACanDelete then\r\n        FItems.Delete(ANode);\r\n      ANode := ANext;\r\n    end;\r\n    if FItems.Count &gt; 0 then\r\n      FFirstFireTime := PJob(FItems.First.Data).NextTime\r\n    else\r\n      FFirstFireTime := 0;\r\n  finally\r\n    FLocker.Leave;\r\n  end;\r\nend;\r\n\r\nfunction TRepeatJobs.Clear(AProc: TJobProc; AData: Pointer; AMaxTimes: Integer): Integer;\r\nbegin\r\n  Result := ClearJobs(nil, AProc, AData, AMaxTimes);\r\nend;\r\n\r\nfunction TRepeatJobs.Clear(AObject: Pointer; AMaxTimes: Integer): Integer;\r\nbegin\r\n  Result := ClearJobs(AObject, nil, nil, AMaxTimes);\r\nend;\r\n\r\nprocedure TRepeatJobs.Clear;\r\nbegin\r\n  FLocker.Enter;\r\n  try\r\n    FItems.Clear;\r\n  finally\r\n    FLocker.Leave;\r\n  end;\r\nend;\r\n\r\nconstructor TRepeatJobs.Create(AOwner: TYXDWorkers);\r\nbegin\r\n  inherited Create(AOwner);\r\n  FLocker := TCriticalSection.Create;\r\n  FItems := TRBTree.Create(DoTimeCompare);\r\n  FItems.OnDelete := DoJobDelete;\r\nend;\r\n\r\ndestructor TRepeatJobs.Destroy;\r\nbegin\r\n  FLocker.Enter;\r\n  try\r\n    FItems.Free;\r\n  finally\r\n    inherited;\r\n    FLocker.Leave;\r\n    FLocker.Free;\r\n  end;\r\nend;\r\n\r\nprocedure TRepeatJobs.DoJobDelete(ATree: TRBTree; ANode: PRBNode);\r\nbegin\r\n  FOwner.FreeJob(ANode.Data);\r\nend;\r\n\r\nfunction TRepeatJobs.DoTimeCompare(P1, P2: Pointer): Integer;\r\nbegin\r\n  Result := PJob(P1).NextTime - PJob(P2).NextTime;\r\nend;\r\n\r\nfunction TRepeatJobs.GetCount: Integer;\r\nbegin\r\n  Result := FItems.Count;\r\nend;\r\n\r\nfunction TRepeatJobs.InternalPop: PJob;\r\nvar\r\n  ANode: PRBNode;\r\n  ATick: Int64;\r\n  AJob: PJob;\r\nbegin\r\n  Result := nil;\r\n  ATick := GetTimestamp;\r\n  FLocker.Enter;\r\n  try\r\n    if FItems.Count &gt; 0 then begin\r\n      ANode := FItems.First;\r\n      if PJob(ANode.Data).NextTime &lt;= ATick then begin\r\n        AJob := ANode.Data;\r\n        if AJob.Next &lt;&gt; nil then \/\/ \u5982\u679c\u6ca1\u6709\u66f4\u591a\u9700\u8981\u6267\u884c\u7684\u4f5c\u4e1a\uff0c\u5219\u5220\u9664\u7ed3\u70b9\uff0c\u5426\u5219\u6307\u5411\u4e0b\u4e00\u4e2a\r\n          ANode.Data := AJob.Next\r\n        else begin\r\n          ANode.Data := nil;\r\n          FItems.Delete(ANode);\r\n          ANode := FItems.First;\r\n          if ANode &lt;&gt; nil then\r\n            FFirstFireTime := PJob(ANode.Data).NextTime\r\n          else \/\/ \u6ca1\u6709\u8ba1\u5212\u4f5c\u4e1a\u4e86\uff0c\u4e0d\u9700\u8981\u4e86\r\n            FFirstFireTime := 0;\r\n        end;\r\n        if AJob.Runonce then\r\n          Result := AJob\r\n        else begin\r\n          Inc(AJob.NextTime, AJob.Interval);\r\n          Result := JobPool.Pop;\r\n          Result.Assign(AJob);\r\n          Result.Source := AJob;\r\n          \/\/ \u91cd\u65b0\u63d2\u5165\u4f5c\u4e1a\r\n          ANode := FItems.Find(AJob);\r\n          if ANode = nil then begin\r\n            FItems.Insert(AJob);\r\n            FFirstFireTime := PJob(FItems.First.Data).NextTime;\r\n          end else begin\/\/ \u5982\u679c\u5df2\u7ecf\u5b58\u5728\u540c\u4e00\u65f6\u523b\u7684\u4f5c\u4e1a\uff0c\u5219\u81ea\u5df1\u6302\u63a5\u5230\u5176\u5b83\u4f5c\u4e1a\u5934\u90e8\r\n            AJob.Next := PJob(ANode.Data);\r\n            ANode.Data := AJob; \/\/ \u9996\u4e2a\u4f5c\u4e1a\u6539\u4e3a\u81ea\u5df1\r\n          end;\r\n        end;\r\n      end;\r\n    end;\r\n  finally\r\n    FLocker.Leave;\r\n  end;\r\n  if Result &lt;&gt; nil then begin\r\n    Result.PopTime := ATick;\r\n    Result.Next := nil;\r\n  end;\r\nend;\r\n\r\nfunction TRepeatJobs.InternalPush(AJob: PJob): Boolean;\r\nvar\r\n  ANode: PRBNode;\r\nbegin\r\n  AJob.UpdateNextTime;  \/\/ \u8ba1\u7b97\u4f5c\u4e1a\u7684\u4e0b\u6b21\u6267\u884c\u65f6\u95f4\r\n  FLocker.Enter;\r\n  try\r\n    ANode := FItems.Find(AJob);\r\n    if ANode = nil then begin\r\n      FItems.Insert(AJob);\r\n      FFirstFireTime := PJob(FItems.First.Data).NextTime;\r\n    end else begin \/\/ \u5982\u679c\u5df2\u7ecf\u5b58\u5728\u540c\u4e00\u65f6\u523b\u7684\u4f5c\u4e1a\uff0c\u5219\u81ea\u5df1\u6302\u63a5\u5230\u5176\u5b83\u4f5c\u4e1a\u5934\u90e8\r\n      AJob.Next := PJob(ANode.Data);\r\n      ANode.Data := AJob; \/\/ \u9996\u4e2a\u4f5c\u4e1a\u6539\u4e3a\u81ea\u5df1\r\n    end;\r\n  finally\r\n    FLocker.Leave;\r\n  end;\r\n  Result := True;\r\nend;\r\n\r\n{ TYXDWorker }\r\n\r\nprocedure TYXDWorker.ComNeeded(AInitFlags: Cardinal);\r\nbegin\r\n  {$IFDEF MSWINDOWS}\r\n  if not ComInitialized then begin\r\n    if AInitFlags = 0 then\r\n      CoInitialize(nil)\r\n    else\r\n      CoInitializeEx(nil, AInitFlags);\r\n    SetValue(WORKER_COM_INITED, True);\r\n  end;\r\n  {$ENDIF MSWINDOWS}\r\nend;\r\n\r\nconstructor TYXDWorker.Create(AOwner: TYXDWorkers);\r\nbegin\r\n  inherited Create(True);\r\n  FOwner := AOwner;\r\n  FTimeout := 1000;\r\n  FFlags := WORKER_ISBUSY; \/\/ \u9ed8\u8ba4\u4e3a\u5fd9\u788c\r\n  AtomicIncrement(AOwner.FBusyCount);\r\n  FEvent := TEvent.Create(nil, False, False, '');\r\n  FreeOnTerminate := True;\r\nend;\r\n\r\ndestructor TYXDWorker.Destroy;\r\nbegin\r\n  FreeAndNil(FEvent);\r\n  inherited;\r\nend;\r\n\r\nprocedure TYXDWorker.DoJob(AJob: PJob);\r\nbegin\r\n  {$IFDEF UNICODE}\r\n  if AJob.IsAnonWorkerProc then\r\n    AJob.WorkerProcA(AJob)\r\n  else\r\n  {$ENDIF}\r\n  AJob.WorkerProc(AJob);\r\nend;\r\n\r\nprocedure TYXDWorker.Execute;\r\nvar\r\n  wr: TWaitResult;\r\n  {$IFDEF MSWINDOWS}\r\n  SyncEvent: TEvent;\r\n  {$ENDIF}\r\nbegin\r\n  {$IFDEF MSWINDOWS}\r\n  SyncEvent := TEvent.Create(nil, False, False, '');\r\n  {$ENDIF}\r\n  try\r\n    while not(Terminated or FOwner.FTerminating) do begin\r\n      if FOwner.Enabled then begin\r\n        if (FOwner.FSimpleJobs.FFirst &lt;&gt; nil) then begin\r\n          {$IFDEF MSWINDOWS}SwitchToThread; {$ELSE} TThread.Yield;{$ENDIF}\r\n          FTimeout := 0;\r\n        end else if (FOwner.FRepeatJobs.FFirstFireTime &lt;&gt; 0) then begin\r\n          FTimeout := FOwner.FRepeatJobs.FFirstFireTime - GetTimestamp;\r\n          if FTimeout &lt; 0 then \/\/ \u65f6\u95f4\u5df2\u7ecf\u5230\u4e86\uff1f\u90a3\u4e48\u7acb\u523b\u6267\u884c\r\n            FTimeout := 0;\r\n        end else\r\n          FTimeout := WAITJOB_TIMEOUT;\r\n      end else\r\n        FTimeout := WAITJOB_TIMEOUT; \/\/ \u5982\u679c\u4ecd\u6ca1\u6709\u4f5c\u4e1a\u8fdb\u5165\uff0c\u5219\u9664\u975e\u81ea\u5df1\u662f\u4fdd\u7559\u7684\u7ebf\u7a0b\u5bf9\u8c61\uff0c\u5426\u5219\u91ca\u653e\u5de5\u4f5c\u8005\r\n\r\n      if FTimeout &lt;&gt; 0 then begin\r\n        wr := FEvent.WaitFor(FTimeout);\r\n        if Terminated or FOwner.FTerminating then\r\n          Break;\r\n      end else\r\n        wr := wrSignaled;\r\n\r\n      if (wr = wrSignaled) or ((FOwner.FRepeatJobs.FFirstFireTime &lt;&gt; 0) and\r\n        (GetTimestamp &gt;= FOwner.FRepeatJobs.FFirstFireTime - 1)) then\r\n      begin\r\n        if FOwner.FTerminating then\r\n          Break;\r\n\r\n        if IsIdle then begin\r\n          SetValue(WORKER_ISBUSY or WORKER_LOOKUP, true);\r\n          AtomicIncrement(FOwner.FBusyCount);\r\n        end else\r\n          SetValue(WORKER_LOOKUP, true);\r\n\r\n        repeat\r\n          FActiveJob := FOwner.Popup;\r\n          if FActiveJob &lt;&gt; nil then begin\r\n            FActiveJob.Worker := Self;\r\n            FActiveJobProc := FActiveJob.WorkerProc;\r\n\r\n            \/\/ \u4e3aClear(AObject)\u51c6\u5907\u5224\u65ad\uff0c\u4ee5\u907f\u514dFActiveJob\u7ebf\u7a0b\u4e0d\u5b89\u5168\r\n            FActiveJobData := FActiveJob.Data;\r\n            if FActiveJob.IsSignalWakeup then\r\n              FActiveJobSource := FActiveJob.Source\r\n            else\r\n              FActiveJobSource := nil;\r\n\r\n            if FActiveJob.IsGrouped then\r\n              FActiveJobGroup := FActiveJob.Group\r\n            else\r\n              FActiveJobGroup := nil;\r\n\r\n            FActiveJobFlags := FActiveJob.Flags;\r\n            if FActiveJob.StartTime = 0 then begin\r\n              FActiveJob.StartTime := GetTimestamp;\r\n              FActiveJob.FirstTime := FActiveJob.StartTime;\r\n            end else\r\n              FActiveJob.StartTime := GetTimestamp;\r\n\r\n            try\r\n              FFlags := (FFlags or WORKER_EXECUTING) and (not WORKER_LOOKUP);\r\n              if FActiveJob.InMainThread then\r\n              {$IFDEF MSWINDOWS}\r\n              begin\r\n                if PostMessage(FOwner.FMainWorker, WM_APP, WPARAM(FActiveJob), LPARAM(SyncEvent)) then\r\n                  SyncEvent.WaitFor(INFINITE);\r\n              end\r\n              {$ELSE}\r\n                Synchronize(Self, FireInMainThread)\r\n              {$ENDIF}\r\n              else\r\n                DoJob(FActiveJob);\r\n            except\r\n              if Assigned(FOwner.FOnErrorNotify) then\r\n                FOwner.FOnErrorNotify(FActiveJob, Exception(ExceptObject), 'TYXDWorker.Execute');\r\n            end;\r\n\r\n            if not FActiveJob.Runonce then\r\n              FOwner.FRepeatJobs.AfterJobRun(FActiveJob, GetTimestamp - FActiveJob.StartTime)\r\n            else begin\r\n              if FActiveJob.IsSignalWakeup then\r\n                FOwner.SignalWorkDone(FActiveJob, GetTimestamp - FActiveJob.StartTime)\r\n              else if FActiveJob.IsLongtimeJob then\r\n                AtomicDecrement(FOwner.FLongTimeWorkers)\r\n              else if FActiveJob.IsGrouped then\r\n                FActiveJobGroup.DoJobExecuted(FActiveJob);\r\n              FActiveJob.Worker := nil;\r\n            end;\r\n\r\n            FOwner.FreeJob(FActiveJob);\r\n            FActiveJobProc := nil;\r\n            FActiveJobSource := nil;\r\n            FActiveJobFlags := 0;\r\n            FActiveJobGroup := nil;\r\n            FTerminatingJob := nil;\r\n            FFlags := FFlags and (not WORKER_EXECUTING);\r\n\r\n          end else\r\n            FFlags := FFlags and (not WORKER_LOOKUP);\r\n        until (FActiveJob = nil) or FOwner.FTerminating or Terminated or (not FOwner.Enabled);\r\n\r\n        SetValue(WORKER_ISBUSY, False);\r\n        FOwner.WorkerIdle(Self, irNoJob);\r\n      end else if (not IsReserved) and (FTimeout = WAITJOB_TIMEOUT) then begin\r\n        SetValue(WORKER_ISBUSY, False);\r\n        FOwner.WorkerIdle(Self, irTimeout);\r\n      end;\r\n    end;\r\n  finally\r\n    FOwner.WorkerTerminate(Self);\r\n    {$IFDEF MSWINDOWS}\r\n    if ComInitialized then\r\n      CoUninitialize;\r\n    FreeAndNil(SyncEvent);\r\n    {$ENDIF}\r\n  end;\r\nend;\r\n\r\nprocedure TYXDWorker.FireInMainThread;\r\nbegin\r\n  DoJob(FActiveJob);\r\nend;\r\n\r\nfunction TYXDWorker.GetIsIdle: Boolean;\r\nbegin\r\n  Result := not IsBusy;\r\nend;\r\n\r\nfunction TYXDWorker.GetValue(Index: Integer): Boolean;\r\nbegin\r\n  Result := (FFlags and Index) &lt;&gt; 0;\r\nend;\r\n\r\nprocedure TYXDWorker.SetValue(Index: Integer; const Value: Boolean);\r\nbegin\r\n  if Value then\r\n    FFlags := (FFlags or Index)\r\n  else\r\n    FFlags := (FFlags and (not Index));\r\nend;\r\n\r\n{ TYXDWorkers }\r\n\r\nfunction TYXDWorkers.Clear(const ASignalName: string): Integer;\r\nbegin\r\n  Result := ClearWaitJobs(0, ASignalName);\r\nend;\r\n\r\nfunction TYXDWorkers.Clear(ASignalId: Integer): Integer;\r\nbegin\r\n  Result := ClearWaitJobs(ASignalId, '');\r\nend;\r\n\r\nfunction TYXDWorkers.Clear(AProc: TJobProc; AData: Pointer; AMaxTimes: Integer): Integer;\r\nbegin\r\n  Result := ClearJobs(nil, AProc, AData, AMaxTimes);\r\nend;\r\n\r\nfunction TYXDWorkers.Clear(AObject: Pointer; AMaxTimes: Integer): Integer;\r\nbegin\r\n  Result := ClearJobs(AObject, nil, nil, AMaxTimes);\r\nend;\r\n\r\nprocedure TYXDWorkers.Clear;\r\nvar\r\n  i: Integer;\r\n  AParam: TWorkerWaitParam;\r\n  ASignal: PSignal;\r\nbegin\r\n  DisableWorkers; \/\/ \u907f\u514d\u5de5\u4f5c\u8005\u53d6\u5f97\u65b0\u7684\u4f5c\u4e1a\r\n  try\r\n    FSimpleJobs.Clear;\r\n    FRepeatJobs.Clear;\r\n    FLocker.Enter;\r\n    try\r\n      for i := 0 to FSignalJobs.BucketCount - 1 do begin\r\n        if Assigned(FSignalJobs.Buckets[i]) then begin\r\n          ASignal := FSignalJobs.Buckets[i].Data;\r\n          FreeJob(ASignal.First);\r\n          ASignal.First := nil;\r\n        end;\r\n      end;\r\n    finally\r\n      FLocker.Leave;\r\n    end;\r\n    AParam.WaitType := $FF;\r\n    WaitRunningDone(AParam);\r\n  finally\r\n    EnableWorkers;\r\n  end;\r\nend;\r\n\r\nfunction TYXDWorkers.ClearJobs(AObject: Pointer; AProc: TJobProc;\r\n  AData: Pointer; AMaxTimes: Integer): Integer;\r\nvar\r\n  ACleared: Integer;\r\n  AWaitParam: TWorkerWaitParam;\r\n\r\n  function ClearSignalJobs(IsClearObject: Boolean): Integer;\r\n  var\r\n    i: Integer;\r\n    AJob, ANext, APrior: PJob;\r\n    AList: PHashList;\r\n    ASignal: PSignal;\r\n    B: Boolean;\r\n  begin\r\n    Result := 0;\r\n    FLocker.Enter;\r\n    try\r\n      for i := 0 to FSignalJobs.BucketCount - 1 do begin\r\n        AList := FSignalJobs.Buckets[i];\r\n        if AList &lt;&gt; nil then begin\r\n          ASignal := AList.Data;\r\n          if ASignal.First &lt;&gt; nil then begin\r\n            AJob := ASignal.First;\r\n            APrior := nil;\r\n            while (AJob &lt;&gt; nil) and (AMaxTimes &lt;&gt; 0) do begin\r\n              ANext := AJob.Next;\r\n              if IsClearObject then\r\n                B := TMethod(AJob.WorkerProc).Data = AObject\r\n              else\r\n                B := SameWorkerProc(AJob.WorkerProc, AProc) and ((AData = Pointer(-1)) or (AJob.Data = AData));\r\n              if B then begin\r\n                if ASignal.First = AJob then\r\n                  ASignal.First := ANext;\r\n                if Assigned(APrior) then\r\n                  APrior.Next := ANext;\r\n                AJob.Next := nil;\r\n                FreeJob(AJob);\r\n                Inc(Result);\r\n                Dec(AMaxTimes);\r\n              end else\r\n                APrior := AJob;\r\n              AJob := ANext;\r\n            end;\r\n            if AMaxTimes = 0 then\r\n              Break;\r\n          end;\r\n        end;\r\n      end;\r\n    finally\r\n      FLocker.Leave;\r\n    end;\r\n  end;\r\n\r\nbegin\r\n  Result := 0;\r\n  if Self &lt;&gt; nil then begin\r\n    ACleared := FSimpleJobs.ClearJobs(AObject, AProc, AData, AMaxTimes);\r\n    Dec(AMaxTimes, ACleared);\r\n    Inc(Result, ACleared);\r\n    if AMaxTimes &lt;&gt; 0 then begin\r\n      ACleared := FRepeatJobs.ClearJobs(AObject, AProc, AData, AMaxTimes);\r\n      Dec(AMaxTimes, ACleared);\r\n      Inc(Result, ACleared);\r\n      if AMaxTimes &lt;&gt; 0 then begin\r\n        ACleared := ClearSignalJobs(AObject &lt;&gt; nil);\r\n        Inc(Result, ACleared);\r\n        if AMaxTimes &lt;&gt; 0 then begin\r\n          AWaitParam.WaitType := 1;\r\n          AWaitParam.Data := AData;\r\n          AWaitParam.WorkerProc := TMethod(AProc);\r\n          WaitRunningDone(AWaitParam);\r\n        end;\r\n      end;\r\n    end;\r\n  end;\r\nend;\r\n\r\nfunction TYXDWorkers.ClearWaitJobs(ASignalId: Integer; const ASignalName: string): Integer;\r\nvar\r\n  i: Integer;\r\n  ASignal: PSignal;\r\n  AJob: PJob;\r\n  B: Boolean;\r\nbegin\r\n  AJob := nil;\r\n  FLocker.Enter;\r\n  try\r\n    for i := 0 to FSignalJobs.BucketCount - 1 do begin\r\n      if FSignalJobs.Buckets[i] &lt;&gt; nil then begin\r\n        ASignal := FSignalJobs.Buckets[i].Data;\r\n        if ASignalId &gt; 0 then\r\n          B := ASignal.Id = ASignalId\r\n        else\r\n          B := ASignal.Name = ASignalName;\r\n        if B then begin\r\n          AJob := ASignal.First;\r\n          ASignal.First := nil;\r\n          Break;\r\n        end;\r\n      end;\r\n    end;\r\n  finally\r\n    FLocker.Leave;\r\n  end;\r\n  if AJob &lt;&gt; nil then\r\n    Result := ClearSignalJobs(AJob)\r\n  else\r\n    Result := 0;\r\nend;\r\n\r\nfunction TYXDWorkers.ClearSignalJobs(ASource: PJob): Integer;\r\nvar\r\n  ACount: Integer;\r\n  AFirst, ALast, APrior, ANext: PJob;\r\n  AWaitParam: TWorkerWaitParam;\r\nbegin\r\n  Result := 0;\r\n  AFirst := nil;\r\n  APrior := nil;\r\n  FSimpleJobs.FLocker.Enter;\r\n  try\r\n    ALast := FSimpleJobs.FFirst;\r\n    ACount := FSimpleJobs.Count;\r\n    FSimpleJobs.FFirst := nil;\r\n    FSimpleJobs.FLast := nil;\r\n    FSimpleJobs.FCount := 0;\r\n  finally\r\n    FSimpleJobs.FLocker.Leave;\r\n  end;\r\n\r\n  while ALast &lt;&gt; nil do begin\r\n    if (ALast.IsSignalWakeup) and (ALast.Source = ASource) then begin\r\n      ANext := ALast.Next;\r\n      ALast.Next := nil;\r\n      FreeJob(ALast);\r\n      ALast := ANext;\r\n      if APrior &lt;&gt; nil then\r\n        APrior.Next := ANext;\r\n      Dec(ACount);\r\n      Inc(Result);\r\n    end else begin\r\n      if AFirst = nil then\r\n        AFirst := ALast;\r\n      APrior := ALast;\r\n      ALast := ALast.Next;\r\n    end;\r\n  end;\r\n\r\n  if ACount &gt; 0 then begin\r\n    FSimpleJobs.FLocker.Enter;\r\n    try\r\n      APrior.Next := FSimpleJobs.FFirst;\r\n      FSimpleJobs.FFirst := AFirst;\r\n      Inc(FSimpleJobs.FCount, ACount);\r\n      if FSimpleJobs.FLast = nil then\r\n        FSimpleJobs.FLast := APrior;\r\n    finally\r\n      FSimpleJobs.FLocker.Leave;\r\n    end;\r\n  end;\r\n  AWaitParam.WaitType := 2;\r\n  AWaitParam.SourceJob := ASource;\r\n  WaitRunningDone(AWaitParam);\r\n  FreeJob(ASource);\r\nend;\r\n\r\nprocedure TYXDWorkers.ClearWorkers;\r\nvar\r\n  i: Integer;\r\n\r\n  {$IFDEF MSWINDOWS}\r\n  function WorkerExists: Boolean;\r\n  var\r\n    J: Integer;\r\n    ACode: Cardinal;\r\n  begin\r\n    Result := False;\r\n    FLocker.Enter;\r\n    try\r\n      while FWorkerCount &gt; 0 do begin\r\n        if GetExitCodeThread(FWorkers[0].Handle, ACode) then begin\r\n          if ACode = STILL_ACTIVE then begin\r\n            Result := True;\r\n            Break;\r\n          end;\r\n        end;\r\n        \/\/ \u5de5\u4f5c\u8005\u5df2\u7ecf\u4e0d\u5b58\u5728\uff0c\u53ef\u80fd\u88ab\u5916\u90e8\u7ebf\u7a0b\u7ed3\u675f\r\n        FreeAndNil(FWorkers[0]);\r\n        if FWorkerCount &gt; 0 then begin\r\n          for J := 1 to FWorkerCount - 1 do\r\n            FWorkers[J - 1] := FWorkers[J];\r\n          Dec(FWorkerCount);\r\n          FWorkers[FWorkerCount] := nil;\r\n        end;\r\n      end;\r\n    finally\r\n      FLocker.Leave;\r\n    end;\r\n  end;\r\n  {$ENDIF}\r\nbegin\r\n  FTerminating := True;\r\n  FLocker.Enter;\r\n  try\r\n    FRepeatJobs.FFirstFireTime := 0;\r\n    for i := 0 to FWorkerCount - 1 do\r\n      FWorkers[i].FEvent.SetEvent;\r\n  finally\r\n    FLocker.Leave;\r\n  end;\r\n  while (FWorkerCount &gt; 0) {$IFDEF MSWINDOWS} and WorkerExists {$ENDIF} do\r\n    {$IFDEF MSWINDOWS}SwitchToThread; {$ELSE} TThread.Yield;{$ENDIF}\r\nend;\r\n\r\nconstructor TYXDWorkers.Create(AMinWorkers: Integer);\r\nvar\r\n  i: Integer;\r\nbegin\r\n  FBusyCount := 0;\r\n  FSimpleJobs := TSimpleJobs.Create(Self);\r\n  FRepeatJobs := TRepeatJobs.Create(Self);\r\n  FSignalJobs := TYXDHashTable.Create(1361);\r\n  FSignalJobs.OnDelete := DoJobFree;\r\n  FSignalJobs.AutoSize := True;\r\n  FLocker := TCriticalSection.Create;\r\n\r\n  FCPUNum := GetCPUCount;\r\n  if AMinWorkers &lt; 1 then\r\n    FMinWorkers := 2\r\n  else\r\n    FMinWorkers := AMinWorkers; \/\/ \u6700\u5c11\u5de5\u4f5c\u8005\u4e3a2\u4e2a\r\n  FMaxWorkers := (FCPUNum shl 1) + 1;\r\n  if FMaxWorkers &lt;= FMinWorkers then\r\n    FMaxWorkers := (FMinWorkers shl 1) + 1;\r\n  FTerminating := False;\r\n\r\n  \/\/ \u521b\u5efa\u9ed8\u8ba4\u5de5\u4f5c\u8005\r\n  FDisableCount := 0;\r\n  FMaxSignalId := 0;\r\n  FWorkerCount := FMinWorkers;\r\n  SetLength(FWorkers, FMaxWorkers);\r\n  for i := 0 to FMinWorkers - 1 do begin\r\n    FWorkers[i] := TYXDWorker.Create(Self);\r\n    FWorkers[i].SetValue(WORKER_RESERVED, True); \/\/ \u4fdd\u7559\uff0c\u4e0d\u9700\u8981\u7a7a\u95f2\u68c0\u67e5\r\n    FWorkers[i].Suspended := False;\r\n    {$IFDEF MSWINDOWS}\r\n    SetThreadCPU(FWorkers[i].Handle, i mod FCPUNum);\r\n    {$ELSE}\r\n    SetThreadCPU(FWorkers[i].ThreadID, i mod FCPUNum);\r\n    {$ENDIF}\r\n  end;\r\n  FMaxLongtimeWorkers := (FMaxWorkers shr 1);\r\n  {$IFDEF MSWINDOWS}\r\n  FMainWorker := AllocateHWnd(DoMainThreadWork);\r\n  {$ENDIF}\r\nend;\r\n\r\ndestructor TYXDWorkers.Destroy;\r\nbegin\r\n  ClearWorkers;\r\n  FLocker.Enter;\r\n  try\r\n    FreeAndNil(FSimpleJobs);\r\n    FreeAndNil(FRepeatJobs);\r\n    FreeAndNil(FSignalJobs);\r\n  finally\r\n    FLocker.Leave;\r\n    FLocker.Free;\r\n  end;\r\n  {$IFDEF MSWINDOWS}\r\n  DeallocateHWnd(FMainWorker);\r\n  {$ENDIF}\r\n  inherited;\r\nend;\r\n\r\nprocedure TYXDWorkers.DisableWorkers;\r\nbegin\r\n  AtomicIncrement(FDisableCount);\r\nend;\r\n\r\nprocedure TYXDWorkers.DoCustomFreeData(AFreeType: TJobDataFreeType;\r\n  var AData: Pointer);\r\nbegin\r\n  if Assigned(FOnCustomFreeData) then\r\n    FOnCustomFreeData(Self, AFreeType, AData);\r\nend;\r\n\r\nprocedure TYXDWorkers.DoJobFree(ATable: TObject; AHash: Cardinal; AData: Pointer);\r\nvar\r\n  ASignal: PSignal;\r\nbegin\r\n  ASignal := AData;\r\n  if ASignal.First &lt;&gt; nil then\r\n    FreeJob(ASignal.First);\r\n  Dispose(ASignal);\r\nend;\r\n\r\n{$IFDEF MSWINDOWS}\r\nprocedure TYXDWorkers.DoMainThreadWork(var AMsg: TMessage);\r\nvar\r\n  AJob: PJob;\r\nbegin\r\n  if AMsg.Msg = WM_APP then begin\r\n    try\r\n      AJob := PJob(AMsg.WPARAM);\r\n      AJob.Worker.DoJob(AJob);\r\n    finally\r\n      if AMsg.LPARAM &lt;&gt; 0 then\r\n        TEvent(AMsg.LPARAM).SetEvent;\r\n    end;\r\n  end else\r\n    AMsg.Result := DefWindowProc(FMainWorker, AMsg.Msg, AMsg.WPARAM, AMsg.LPARAM);\r\nend;\r\n{$ENDIF}\r\n\r\nprocedure TYXDWorkers.EnableWorkers;\r\nvar\r\n  ANeedCount: Integer;\r\nbegin\r\n  if AtomicDecrement(FDisableCount) = 0 then begin\r\n    if (FSimpleJobs.Count &gt; 0) or (FRepeatJobs.Count &gt; 0) then begin\r\n      ANeedCount := FSimpleJobs.Count + FRepeatJobs.Count;\r\n      while ANeedCount &gt; 0 do begin\r\n        if not LookupIdleWorker then\r\n          Break;\r\n        Dec(ANeedCount);\r\n      end;\r\n    end;\r\n  end;\r\nend;\r\n\r\nprocedure InitJobFreeType(AOwner: TYXDWorkers; AJob: PJob; AData: Pointer; AFreeType: TJobDataFreeType); inline;\r\nbegin\r\n  if AData &lt;&gt; nil then begin\r\n    case AFreeType of\r\n      jdfFreeAsObject:\r\n        AJob.IsObjectOwner := True;\r\n      jdfFreeAsRecord:\r\n        AJob.IsRecordOwner := True;\r\n      jdfFreeAsInterface:\r\n        begin\r\n          AJob.IsInterfaceOwner := True;\r\n          IUnknown(AData)._AddRef;\r\n        end;\r\n    end;\r\n  end else begin\r\n    if AFreeType &lt;&gt; jdfFreeByUser then\r\n      AOwner.DoCustomFreeData(AFreeType, AData);\r\n  end;\r\nend;\r\n\r\nprocedure TYXDWorkers.FireSignalJob(ASignal: PSignal; AData: Pointer;\r\n  AFreeType: TJobDataFreeType);\r\nvar\r\n  AJob, ACopy: PJob;\r\n  ACount: PInteger;\r\nbegin\r\n  Inc(ASignal.Fired);\r\n  if AData &lt;&gt; nil then begin\r\n    New(ACount);\r\n    ACount^ := 1; \/\/\u521d\u59cb\u503c\r\n  end else\r\n    ACount := nil;\r\n  AJob := ASignal.First;\r\n  while AJob &lt;&gt; nil do begin\r\n    ACopy := JobPool.Pop;\r\n    ACopy.Assign(AJob);\r\n    ACopy.SetValue(JOB_RUN_ONCE, True);\r\n    ACopy.Source := AJob;\r\n    ACopy.Data := AData;\r\n    InitJobFreeType(Self, ACopy, AData, AFreeType);\r\n    if ACount &lt;&gt; nil then begin\r\n      AtomicIncrement(ACount^);\r\n      ACopy.RefCount := ACount;\r\n    end;\r\n    FSimpleJobs.Push(ACopy);\r\n    AJob := AJob.Next;\r\n  end;\r\n  if AData &lt;&gt; nil then begin\r\n    if AtomicDecrement(ACount^) = 0 then begin\r\n      Dispose(ACount);\r\n      FreeJobData(AData, AFreeType);\r\n    end;\r\n  end;\r\nend;\r\n\r\nprocedure TYXDWorkers.FreeJob(AJob: PJob);\r\nvar\r\n  ANext: PJob;\r\n  AFreeData: Boolean;\r\nbegin\r\n  while AJob &lt;&gt; nil do begin\r\n    ANext := AJob.Next;\r\n    if AJob.Data &lt;&gt; nil then begin\r\n      if AJob.IsSignalWakeup then begin\r\n        AFreeData := AtomicDecrement(AJob.RefCount^) = 0;\r\n        if AFreeData then\r\n          Dispose(AJob.RefCount);\r\n      end else\r\n        AFreeData := AJob.IsDataOwner;\r\n      if AFreeData then begin\r\n        if AJob.IsObjectOwner then begin\r\n          FreeJobData(AJob.Data, jdfFreeAsObject);\r\n          AJob.Data := nil;\r\n        end else if AJob.IsRecordOwner then begin\r\n          FreeJobData(AJob.Data, jdfFreeAsRecord);\r\n          AJob.Data := nil;\r\n        end else if AJob.IsInterfaceOwner then begin\r\n          FreeJobData(AJob.Data, jdfFreeAsInterface);\r\n          AJob.Data := nil;\r\n        end;\r\n      end;\r\n    end;\r\n    JobPool.Push(AJob);\r\n    AJob := ANext;\r\n  end;\r\nend;\r\n\r\nprocedure TYXDWorkers.FreeJobData(AData: Pointer; AFreeType: TJobDataFreeType);\r\nbegin\r\n  case AFreeType of\r\n    jdfFreeAsObject:\r\n      try\r\n        FreeAndNil(TObject(AData));\r\n      except\r\n        if Assigned(FOnErrorNotify) then\r\n          FOnErrorNotify(nil, Exception(ExceptObject), 'Workers.FreeJobData');\r\n      end;\r\n    jdfFreeAsRecord:\r\n      try\r\n        Dispose(AData);\r\n      except\r\n        if Assigned(FOnErrorNotify) then\r\n          FOnErrorNotify(nil, Exception(ExceptObject), 'Workers.FreeJobData');\r\n      end;\r\n    jdfFreeAsInterface:\r\n      try\r\n        IUnknown(AData)._Release;\r\n      except\r\n        if Assigned(FOnErrorNotify) then\r\n          FOnErrorNotify(nil, Exception(ExceptObject), 'Workers.FreeJobData');\r\n      end;\r\n  end;\r\nend;\r\n\r\nfunction TYXDWorkers.GetEnabled: Boolean;\r\nbegin\r\n  Result := (FDisableCount = 0);\r\nend;\r\n\r\nclass function TYXDWorkers.GetInstance: TYXDWorkers;\r\nbegin\r\n  if not Assigned(Workers) then\r\n    Workers := TYXDWorkers.Create();\r\n  Result := Workers;\r\nend;\r\n\r\nclass function TYXDWorkers.JobPoolCount: Integer;\r\nbegin\r\n  Result := JobPool.Count;\r\nend;\r\n\r\nfunction TYXDWorkers.LookupIdleWorker: Boolean;\r\nvar\r\n  i: Integer;\r\n  AWorker: TYXDWorker;\r\nbegin\r\n  Result := False;\r\n  if (FDisableCount &lt;&gt; 0) or (FBusyCount = MaxWorkers) or FTerminating then\r\n    Exit;\r\n  FLocker.Enter;\r\n  try\r\n    if FBusyCount &lt; FWorkerCount then begin\r\n      for i := 0 to FWorkerCount - 1 do begin\r\n        AWorker := FWorkers[i];\r\n        if (AWorker &lt;&gt; nil) and (AWorker.IsIdle) then begin\r\n          AWorker.Suspended := False;\r\n          AWorker.SetValue(WORKER_ISBUSY, true);\r\n          AtomicIncrement(FBusyCount);\r\n          AWorker.FEvent.SetEvent;\r\n          Result := true;\r\n          Exit;\r\n        end;\r\n      end;\r\n    end;\r\n    if (not Result) and (FWorkerCount &lt; MaxWorkers) then begin\r\n      AWorker := TYXDWorker.Create(Self);\r\n      {$IFDEF MSWINDOWS}\r\n      SetThreadCPU(AWorker.Handle, FWorkerCount mod FCPUNum);\r\n      {$ELSE}\r\n      SetThreadCPU(AWorker.ThreadID, FWorkerCount mod GetCPUCount);\r\n      {$ENDIF}\r\n      AWorker.Suspended := False;\r\n      AWorker.FEvent.SetEvent;\r\n      FWorkers[FWorkerCount] := AWorker;\r\n      Inc(FWorkerCount);\r\n      Result := true;\r\n    end;\r\n  finally\r\n    FLocker.Leave;\r\n  end;\r\nend;\r\n\r\nfunction TYXDWorkers.Popup: PJob;\r\nbegin\r\n  Result := FSimpleJobs.Pop;\r\n  if Result = nil then\r\n    Result := FRepeatJobs.Pop;\r\nend;\r\n\r\nprocedure InitJob(AJob: PJob; AData: Pointer;\r\n  ARunInMainThread: Boolean; const ADelay, AInterval: Int64); inline;\r\nbegin\r\n  AJob.Data := AData;\r\n  if AInterval &gt; 0 then begin\r\n    AJob.Interval := AInterval;\r\n    AJob.SetValue(JOB_RUN_ONCE, False);\r\n  end else\r\n    AJob.SetValue(JOB_RUN_ONCE, True);\r\n  AJob.FirstDelay := ADelay;\r\n  AJob.SetValue(JOB_IN_MAINTHREAD, ARunInMainThread);\r\nend;\r\n\r\nfunction TYXDWorkers.Post(AJobProc: TJobProc; AData: Pointer;\r\n  ARunInMainThread: Boolean; const ADelay, AInterval: Int64;\r\n  AFreeType: TJobDataFreeType): Boolean;\r\nvar\r\n  AJob: PJob;\r\nbegin\r\n  AJob := JobPool.Pop;\r\n  AJob.WorkerProc := AJobProc;\r\n  InitJob(AJob, AData, ARunInMainThread, ADelay, AInterval);\r\n  InitJobFreeType(Self, AJob, AData, AFreeType);\r\n  Result := Post(AJob);\r\nend;\r\n\r\nfunction TYXDWorkers.Post(AJobProc: TJobProcG; AData: Pointer;\r\n  ARunInMainThread: Boolean; const ADelay, AInterval: Int64;\r\n  AFreeType: TJobDataFreeType): Boolean;\r\nvar\r\n  AJob: PJob;\r\nbegin\r\n  AJob := JobPool.Pop;\r\n  AJob.WorkerProc := MakeJobProc(AJobProc);\r\n  InitJob(AJob, AData, ARunInMainThread, ADelay, AInterval);\r\n  InitJobFreeType(Self, AJob, AData, AFreeType);\r\n  Result := Post(AJob);\r\nend;\r\n\r\n{$IFDEF UNICODE}\r\nfunction TYXDWorkers.Post(AJobProc: TJobProcA; AData: Pointer;\r\n  ARunInMainThread: Boolean; const ADelay, AInterval: Int64;\r\n  AFreeType: TJobDataFreeType): Boolean;\r\nvar\r\n  AJob: PJob;\r\nbegin\r\n  AJob := JobPool.Pop;\r\n  AJob.WorkerProcA := AJobProc;\r\n  InitJob(AJob, AData, ARunInMainThread, ADelay, AInterval);\r\n  InitJobFreeType(Self, AJob, AData, AFreeType);\r\n  Result := Post(AJob);\r\nend;\r\n{$ENDIF}\r\n\r\nfunction TimeToDelay(const ATime: TDateTime): Int64; inline;\r\nvar\r\n  ANow, ATemp: TDateTime;\r\nbegin\r\n  ANow := Now;\r\n  ANow := ANow - Trunc(ANow); \/\/ ATime\u6211\u4eec\u53ea\u8981\u65f6\u95f4\u90e8\u5206\uff0c\u65e5\u671f\u5ffd\u7565\r\n  ATemp := ATime - Trunc(ATime);\r\n  if ANow &gt; ATemp then \/\/ \u597d\u5427\uff0c\u4eca\u5929\u7684\u70b9\u5df2\u7ecf\u8fc7\u4e86\uff0c\u7b97\u660e\u5929\r\n    Result := Trunc(((1 + ANow) - ATemp) * WODay) \/\/ \u5ef6\u8fdf\u7684\u65f6\u95f4\uff0c\u5355\u4f4d\u4e3a1ms\r\n  else\r\n    Result := Trunc((ATemp - ANow) * WODay);\r\nend;\r\n\r\nfunction TYXDWorkers.Post(AJobProc: TJobProcG; const ATime: TDateTime;\r\n  const AInterval: Int64; AData: Pointer; ARunInMainThread: Boolean;\r\n  AFreeType: TJobDataFreeType): Boolean;\r\nvar\r\n  AJob: PJob;\r\nbegin\r\n  AJob := JobPool.Pop;\r\n  AJob.WorkerProc := MakeJobProc(AJobProc);\r\n  InitJob(AJob, AData, ARunInMainThread, TimeToDelay(ATime), AInterval);\r\n  InitJobFreeType(Self, AJob, AData, AFreeType);\r\n  Result := Post(AJob);\r\nend;\r\n\r\nfunction TYXDWorkers.Post(AJobProc: TJobProc; const ATime: TDateTime;\r\n  const AInterval: Int64; AData: Pointer; ARunInMainThread: Boolean;\r\n  AFreeType: TJobDataFreeType): Boolean;\r\nvar\r\n  AJob: PJob;\r\nbegin\r\n  AJob := JobPool.Pop;\r\n  AJob.WorkerProc := AJobProc;\r\n  InitJob(AJob, AData, ARunInMainThread, TimeToDelay(ATime), AInterval);\r\n  InitJobFreeType(Self, AJob, AData, AFreeType);\r\n  Result := Post(AJob);\r\nend;\r\n\r\n{$IFDEF UNICODE}\r\nfunction TYXDWorkers.Post(AJobProc: TJobProcA; const ATime: TDateTime;\r\n  const AInterval: Int64; AData: Pointer; ARunInMainThread: Boolean;\r\n  AFreeType: TJobDataFreeType): Boolean;\r\nvar\r\n  AJob: PJob;\r\nbegin\r\n  AJob := JobPool.Pop;\r\n  AJob.WorkerProcA := AJobProc;\r\n  InitJob(AJob, AData, ARunInMainThread, TimeToDelay(ATime), AInterval);\r\n  InitJobFreeType(Self, AJob, AData, AFreeType);\r\n  Result := Post(AJob);\r\nend;\r\n{$ENDIF}\r\n\r\nprocedure InitLogJob(AJob: PJob; AData: Pointer); inline;\r\nbegin\r\n  AJob.Data := AData;\r\n  AJob.SetValue(JOB_LONGTIME, True);\r\n  AJob.SetValue(JOB_RUN_ONCE, True); \/\/ \u957f\u4f5c\u4e1a\u53ea\u8fd0\u884c\u4e00\u6b21\r\nend;\r\n\r\nfunction TYXDWorkers.PostLongJob(AJobProc: TJobProc; AData: Pointer;\r\n  AFreeType: TJobDataFreeType): Boolean;\r\nvar\r\n  AJob: PJob;\r\nbegin\r\n  if AtomicIncrement(FLongTimeWorkers) &lt;= FMaxLongtimeWorkers then begin\r\n    Result := True;\r\n    AJob := JobPool.Pop;\r\n    AJob.WorkerProc := AJobProc;\r\n    InitLogJob(AJob, AData);\r\n    InitJobFreeType(self, AJob, AData, AFreeType);\r\n    Post(AJob);\r\n  end else begin \/\/ \u957f\u671f\u4f5c\u4e1a\u6570\u5df2\u7ecf\u8fbe\u5230\u6781\u9650\r\n    AtomicDecrement(FLongTimeWorkers);\r\n    Result := False;\r\n  end;\r\nend;\r\n\r\nfunction TYXDWorkers.PostLongJob(AJobProc: TJobProcG; AData: Pointer;\r\n  AFreeType: TJobDataFreeType): Boolean;\r\nbegin\r\n  Result := PostLongJob(MakeJobProc(AJobProc), AData, AFreeType);\r\nend;\r\n\r\n{$IFDEF UNICODE}\r\nfunction TYXDWorkers.PostLongJob(AJobProc: TJobProcA; AData: Pointer;\r\n  AFreeType: TJobDataFreeType): Boolean;\r\nvar\r\n  AJob: PJob;\r\nbegin\r\n  if AtomicIncrement(FLongTimeWorkers) &lt;= FMaxLongtimeWorkers then begin\r\n    Result := True;\r\n    AJob := JobPool.Pop;\r\n    AJob.WorkerProcA := AJobProc;\r\n    InitLogJob(AJob, AData);\r\n    InitJobFreeType(self, AJob, AData, AFreeType);\r\n    Post(AJob);\r\n  end else begin \/\/ \u957f\u671f\u4f5c\u4e1a\u6570\u5df2\u7ecf\u8fbe\u5230\u6781\u9650\r\n    AtomicDecrement(FLongTimeWorkers);\r\n    Result := False;\r\n  end;\r\nend;\r\n{$ENDIF}\r\n\r\nprocedure InitWaitJob(AJob: PJob; ASignalId: Integer; ARunInMainThread: Boolean); inline;\r\nbegin\r\n  AJob.Data := nil;\r\n  AJob.SignalId := ASignalId;\r\n  AJob.PushTime := GetTimestamp;\r\n  AJob.SetValue(JOB_SIGNAL_WAKEUP, True);\r\n  AJob.SetValue(JOB_IN_MAINTHREAD, ARunInMainThread);\r\nend;\r\n\r\nfunction TYXDWorkers.PostWaitJob(AJob: PJob; ASignalId: Integer): Boolean;\r\nvar\r\n  ASignal: PSignal;\r\nbegin\r\n  Result := False;\r\n  FLocker.Enter;\r\n  try\r\n    ASignal := FSignalJobs.FindFirstData(ASignalId);\r\n    if ASignal &lt;&gt; nil then begin\r\n      AJob.Next := ASignal.First;\r\n      ASignal.First := AJob;\r\n      Result := True;\r\n    end;\r\n  finally\r\n    FLocker.Leave;\r\n    if not Result then\r\n      JobPool.Push(AJob);\r\n  end;\r\nend;\r\n\r\nfunction TYXDWorkers.PostWait(AJobProc: TJobProc; ASignalId: Integer;\r\n  ARunInMainThread: Boolean): Boolean;\r\nvar\r\n  AJob: PJob;\r\nbegin\r\n  if not FTerminating then begin\r\n    AJob := JobPool.Pop;\r\n    AJob.WorkerProc := AJobProc;\r\n    InitWaitJob(AJob, ASignalId, ARunInMainThread);\r\n    Result := PostWaitJob(AJob, ASignalId);\r\n  end else\r\n    Result := False;\r\nend;\r\n\r\nfunction TYXDWorkers.PostWait(AJobProc: TJobProcG; ASignalId: Integer;\r\n  ARunInMainThread: Boolean): Boolean;\r\nbegin\r\n  Result := PostWait(MakeJobProc(AJobProc), ASignalId, ARunInMainThread);\r\nend;\r\n\r\n{$IFDEF UNICODE}\r\nfunction TYXDWorkers.PostWait(AJobProc: TJobProcA; ASignalId: Integer;\r\n  ARunInMainThread: Boolean): Boolean;\r\nvar\r\n  AJob: PJob;\r\nbegin\r\n  if not FTerminating then begin\r\n    AJob := JobPool.Pop;\r\n    AJob.WorkerProcA := AJobProc;\r\n    InitWaitJob(AJob, ASignalId, ARunInMainThread);\r\n    Result := PostWaitJob(AJob, ASignalId);\r\n  end else\r\n    Result := False;\r\nend;\r\n{$ENDIF}\r\n\r\nfunction TYXDWorkers.Post(AJob: PJob): Boolean;\r\nbegin\r\n  if (not FTerminating) and (Assigned(AJob.WorkerProc)\r\n    {$IFDEF UNICODE} or Assigned(AJob.WorkerProcA){$ENDIF}) then\r\n  begin\r\n    if AJob.Runonce and (AJob.FirstDelay = 0) then\r\n      Result := FSimpleJobs.Push(AJob)\r\n    else\r\n      Result := FRepeatJobs.Push(AJob);\r\n    if Result then\r\n      LookupIdleWorker;\r\n  end else begin\r\n    AJob.Next := nil;\r\n    FreeJob(AJob);\r\n    Result := False;\r\n  end;\r\nend;\r\n\r\nprocedure TYXDWorkers.SendSignal(AId: Integer; AData: Pointer;\r\n  AFreeType: TJobDataFreeType);\r\nvar\r\n  AFound: Boolean;\r\n  ASignal: PSignal;\r\nbegin\r\n  AFound := False;\r\n  if FTerminating then Exit;\r\n  FLocker.Enter;\r\n  try\r\n    ASignal := FSignalJobs.FindFirstData(AId);\r\n    if ASignal &lt;&gt; nil then begin\r\n      AFound := True;\r\n      FireSignalJob(ASignal, AData, AFreeType);\r\n    end;\r\n  finally\r\n    FLocker.Leave;\r\n  end;\r\n  if AFound then\r\n    LookupIdleWorker;\r\nend;\r\n\r\nprocedure TYXDWorkers.SendSignal(const AName: string; AData: Pointer;\r\n  AFreeType: TJobDataFreeType);\r\nvar\r\n  i: Integer;\r\n  ASignal: PSignal;\r\n  AFound: Boolean;\r\nbegin\r\n  AFound := False;\r\n  if Length(AName) = 0 then Exit;\r\n  FLocker.Enter;\r\n  try\r\n    for i := 0 to FSignalJobs.BucketCount - 1 do begin\r\n      if FSignalJobs.Buckets[i] &lt;&gt; nil then begin\r\n        ASignal := FSignalJobs.Buckets[i].Data;\r\n        if (Length(ASignal.Name) = Length(AName)) and (ASignal.Name = AName) then begin\r\n          AFound := True;\r\n          FireSignalJob(ASignal, AData, AFreeType);\r\n          Break;\r\n        end;\r\n      end;\r\n    end;\r\n  finally\r\n    FLocker.Leave;\r\n  end;\r\n  if AFound then\r\n    LookupIdleWorker;\r\nend;\r\n\r\nprocedure TYXDWorkers.SetEnabled(const Value: Boolean);\r\nbegin\r\n  if Value then\r\n    EnableWorkers\r\n  else\r\n    DisableWorkers;\r\nend;\r\n\r\nprocedure TYXDWorkers.SetMaxLongtimeWorkers(const Value: Integer);\r\nbegin\r\n  if FMaxLongtimeWorkers &lt;&gt; Value then begin\r\n    if Value &gt; (MaxWorkers shr 1) then \/\/ \u957f\u65f6\u95f4\u8fd0\u884c\u7684\u4f5c\u4e1a\u4e0d\u80fd\u5927\u4e8e\u6700\u5927\u5de5\u4f5c\u7ebf\u7a0b\u7684\u4e00\u534a\r\n      raise Exception.Create(STooManyLongtimeWorker);\r\n    FMaxLongtimeWorkers := Value;\r\n  end;\r\nend;\r\n\r\nprocedure TYXDWorkers.SetMaxWorkers(const Value: Integer);\r\nvar\r\n  ATemp, AMaxLong: Integer;\r\nbegin\r\n  if (Value &gt;= 2) and (FMaxWorkers &lt;&gt; Value) then begin\r\n    AtomicExchange(ATemp, FLongTimeWorkers);\r\n    AtomicExchange(FLongTimeWorkers, 0); \/\/ \u5f3a\u5236\u7f6e0\uff0c\u9632\u6b62\u6709\u65b0\u5165\u7684\u957f\u65f6\u95f4\u4f5c\u4e1a\r\n    AMaxLong := Value shr 1;\r\n    FLocker.Enter;\r\n    try\r\n      if FLongTimeWorkers &lt; AMaxLong then begin \/\/ \u5df2\u7ecf\u8fdb\u884c\u7684\u957f\u65f6\u95f4\u4f5c\u4e1a\u6570\u5c0f\u4e8e\u4e00\u534a\u7684\u5de5\u4f5c\u8005\r\n        if ATemp &lt; AMaxLong then\r\n          AMaxLong := ATemp;                    \/\/ \u957f\u65f6\u95f4\u4f5c\u4e1a\u6700\u5927\u503c\u4f7f\u7528\u66f4\u6539\u4e4b\u524d\u5df2\u7ecf\u5b58\u5728\u7684\u957f\u65f6\u95f4\u4f5c\u4e1a\u6570\u91cf\r\n        if FMaxWorkers &gt; Value then begin\r\n          while Value &lt; FWorkerCount do         \/\/ \u4e2d\u6b62\u5927\u4e8e\u6700\u5927\u4f5c\u4e1a\u6570\u7684\u4f5c\u4e1a\uff0c\u8c01\u662f\u5012\u9709\u5b69\u5b50\uff1f\r\n            WorkerTerminate(FWorkers[FWorkerCount - 1]);\r\n          FMaxWorkers := Value;\r\n          SetLength(FWorkers, Value);\r\n        end else begin\r\n          FMaxWorkers := Value;\r\n          SetLength(FWorkers, Value);\r\n        end;\r\n      end;\r\n    finally\r\n      FLocker.Leave;\r\n      AtomicExchange(FLongTimeWorkers, AMaxLong);\r\n    end;\r\n  end;\r\nend;\r\n\r\nprocedure TYXDWorkers.SetMinWorkers(const Value: Integer);\r\nbegin\r\n  if FMinWorkers &lt;&gt; Value then begin\r\n    if Value &lt; 1 then\r\n      raise Exception.Create(STooFewWorkers);\r\n    FMinWorkers := Value;\r\n  end;\r\nend;\r\n\r\nfunction TYXDWorkers.SignalIdByName(const AName: string): Integer;\r\nvar\r\n  i, j: Integer;\r\n  ASignal: PSignal;\r\nbegin\r\n  Result := -1;\r\n  j := Length(AName);\r\n  if j &lt; 1 then Exit;\r\n  for i := 0 to FSignalJobs.BucketCount - 1 do begin\r\n    if FSignalJobs.Buckets[i] &lt;&gt; nil then begin\r\n      ASignal := FSignalJobs.Buckets[i].Data;\r\n      if (Length(ASignal.Name) = j) and (ASignal.Name = AName) then begin\r\n        Result := ASignal.Id;\r\n        Exit;\r\n      end;\r\n    end;\r\n  end;\r\nend;\r\n\r\nprocedure TYXDWorkers.SignalWorkDone(AJob: PJob; AUsedTime: Int64);\r\nvar\r\n  ASignal: PSignal;\r\n  ATemp, APrior: PJob;\r\nbegin\r\n  APrior := nil;\r\n  FLocker.Enter;\r\n  try\r\n    ASignal := FSignalJobs.FindFirstData(AJob.SignalId);\r\n    ATemp := ASignal.First;\r\n    while ATemp &lt;&gt; nil do begin\r\n      if ATemp = AJob.Source then begin\r\n        if AJob.IsTerminated then begin\r\n          if APrior &lt;&gt; nil then\r\n            APrior.Next := ATemp.Next\r\n          else\r\n            ASignal.First := ATemp.Next;\r\n          ATemp.Next := nil;\r\n          FreeJob(ATemp);\r\n        end else begin\r\n          Inc(ATemp.Runs);  \/\/ \u66f4\u65b0\u4fe1\u53f7\u4f5c\u4e1a\u7684\u7edf\u8ba1\u4fe1\u606f\r\n          if AUsedTime &gt; 0 then begin\r\n            if ATemp.MinUsedTime = 0 then\r\n              ATemp.MinUsedTime := AUsedTime\r\n            else if AUsedTime &lt; ATemp.MinUsedTime then\r\n              ATemp.MinUsedTime := AUsedTime;\r\n            if ATemp.MaxUsedTime = 0 then\r\n              ATemp.MaxUsedTime := AUsedTime\r\n            else if AUsedTime &gt; ATemp.MaxUsedTime then\r\n              ATemp.MaxUsedTime := AUsedTime;\r\n            Break;\r\n          end;\r\n        end;\r\n      end;\r\n      APrior := ATemp;\r\n      ATemp := ATemp.Next;\r\n    end;\r\n  finally\r\n    FLocker.Leave;\r\n  end;\r\nend;\r\n\r\nprocedure TYXDWorkers.WaitRunningDone(const AParam: TWorkerWaitParam);\r\nvar\r\n  AInMainThread: Boolean;\r\n\r\n  function HasJobRunning: Boolean;\r\n  var\r\n    i: Integer;\r\n    AJob: PJob;\r\n  begin\r\n    Result := False;\r\n    DisableWorkers;\r\n    FLocker.Enter;\r\n    try\r\n      for i := 0 to FWorkerCount - 1 do begin\r\n        if FWorkers[i].IsLookuping then begin\/\/ \u8fd8\u672a\u5c31\u7eea\uff0c\u6240\u4ee5\u5728\u4e0b\u6b21\u67e5\u8be2\r\n          Result := True;\r\n          Break;\r\n        end else if FWorkers[i].IsExecuting then begin\r\n          AJob := FWorkers[i].FActiveJob;\r\n          case AParam.WaitType of\r\n            0: \/\/ ByObject\r\n              Result := TMethod(FWorkers[i].FActiveJobProc).Data = AParam.Bound;\r\n            1: \/\/ ByData\r\n              Result := (TMethod(FWorkers[i].FActiveJobProc).Code = TMethod(AParam.WorkerProc).Code) and\r\n                (TMethod(FWorkers[i].FActiveJobProc).Data = TMethod(AParam.WorkerProc).Data) and\r\n                ((AParam.Data = Pointer(-1)) or\r\n                (FWorkers[i].FActiveJobData = AParam.Data));\r\n            2: \/\/ BySignalSource\r\n              Result := (FWorkers[i].FActiveJobSource = AParam.SourceJob);\r\n            3: \/\/ ByGroup\r\n              Result := (FWorkers[i].FActiveJobGroup = AParam.Group);\r\n            $FF: \/\/ \u6240\u6709\r\n              Result := True;\r\n          else\r\n            if Assigned(FOnErrorNotify) then\r\n              FOnErrorNotify(AJob, Exception.CreateFmt(SBadWaitDoneParam, [AParam.WaitType]), 'TYXDWorkers.WaitRunningDone');\r\n          end;\r\n          if Result then begin\r\n            FWorkers[i].FTerminatingJob := AJob;\r\n            Break;\r\n          end;\r\n        end;\r\n      end;\r\n    finally\r\n      FLocker.Leave;\r\n      EnableWorkers;\r\n    end;\r\n  end;\r\n\r\nbegin\r\n  AInMainThread := GetCurrentThreadId = MainThreadId;\r\n  while True do begin\r\n    if HasJobRunning then begin\r\n      if AInMainThread then begin\r\n        \/\/ \u5982\u679c\u662f\u5728\u4e3b\u7ebf\u7a0b\u4e2d\u6e05\u7406\uff0c\u7531\u4e8e\u4f5c\u4e1a\u53ef\u80fd\u5728\u4e3b\u7ebf\u7a0b\u6267\u884c\uff0c\u53ef\u80fd\u5df2\u7ecf\u6295\u5bc4\u5c1a\u672a\u6267\u884c\uff0c\u6240\u4ee5\u5fc5\u9700\u8ba9\u5176\u80fd\u591f\u6267\u884c\r\n        {$IFDEF NEXTGEN}\r\n        fmx.Forms.Application.ProcessMessages;\r\n        {$ELSE}\r\n        Forms.Application.ProcessMessages;\r\n        {$ENDIF}\r\n      end;\r\n      {$IFDEF MSWINDOWS}\r\n      SwitchToThread;\r\n      {$ELSE}\r\n      TThread.Yield;\r\n      {$ENDIF}\r\n    end else \/\/ \u6ca1\u627e\u5230\r\n      Break;\r\n  end;\r\nend;\r\n\r\nprocedure TYXDWorkers.WorkerIdle(AWorker: TYXDWorker; AReason: TWorkerIdleReason);\r\nvar\r\n  i, J: Integer;\r\nbegin\r\n  if AtomicDecrement(FBusyCount) &gt; FMinWorkers then begin\r\n    FLocker.Enter;\r\n    try  \/\/ \u5de5\u4f5c\u8005\u95f2\u7f6e\u65f6\uff0c\u5982\u679c\u4e0d\u662f\u524d\u4e24\u4e2a\u957f\u5de5\uff0c\u5c31\u68c0\u6d4b\u662f\u5426\u6709\u77ed\u5de5\u53ef\u4ee5\u89e3\u96c7\r\n      if (AWorker &lt;&gt; FWorkers[0]) and (AWorker &lt;&gt; FWorkers[1]) and (AReason = irTimeout) then\r\n      begin\r\n        for i := FMinWorkers to FWorkerCount - 1 do begin \/\/ \u4ece\u7b2c\u4e00\u4e2a\u77ed\u5de5\u5f00\u59cb\uff0c\u5c06\u4ed6\u4eec\u89e3\u96c7\r\n          if AWorker = FWorkers[i] then begin\r\n            AWorker.Terminate;\r\n            for J := i + 1 to FWorkerCount - 1 do\r\n              FWorkers[J - 1] := FWorkers[J];\r\n            FWorkers[FWorkerCount - 1] := nil;\r\n            Dec(FWorkerCount);\r\n            Break;\r\n          end;\r\n        end;\r\n      end;\r\n    finally\r\n      FLocker.Leave;\r\n    end;\r\n  end;\r\n  if AReason &lt;&gt; irNoJob then\r\n    AtomicIncrement(FBusyCount)\r\nend;\r\n\r\nprocedure TYXDWorkers.WorkerTerminate(AWorker: TObject);\r\nvar\r\n  i ,J: Integer;\r\nbegin\r\n  AtomicDecrement(FBusyCount);\r\n  FLocker.Enter;\r\n  for i := 0 to FWorkerCount - 1 do begin \/\/ \u5de5\u4f5c\u8005\u88ab\u4e2d\u6b62\u65f6\uff0c\u4ece\u5217\u8868\u4e2d\u6e05\u9664\r\n    if FWorkers[i] = AWorker then begin\r\n      \/\/System.Move(FWorkers[I + 1], FWorkers[I], (FWorkerCount - I) * SizeOf(TObject));\r\n      for J := i to FWorkerCount - 2 do\r\n        FWorkers[J] := FWorkers[J + 1];\r\n      FWorkers[FWorkerCount - 1] := nil;\r\n      Dec(FWorkerCount);\r\n      Break;\r\n    end;\r\n  end;\r\n  FLocker.Leave;\r\nend;\r\n\r\nfunction TYXDWorkers.RegisterSignal(const AName: string): Integer;\r\nvar\r\n  ASignal: PSignal;\r\nbegin\r\n  FLocker.Enter;\r\n  try\r\n    Result := SignalIdByName(AName);\r\n    if Result &lt; 0 then begin\r\n      Inc(FMaxSignalId);\r\n      New(ASignal);\r\n      ASignal.Id := FMaxSignalId;\r\n      ASignal.Fired := 0;\r\n      ASignal.Name := AName;\r\n      ASignal.First := nil;\r\n      FSignalJobs.Add(ASignal, ASignal.Id);\r\n      Result := ASignal.Id;\r\n    end;\r\n  finally\r\n    FLocker.Leave;\r\n  end;\r\nend;\r\n\r\n{ TJobGroup }\r\n\r\nfunction TJobGroup.Add(AProc: TJobProc; AData: Pointer;\r\n  AInMainThread: Boolean; AFreeType: TJobDataFreeType): Boolean;\r\nvar\r\n  AJob: PJob;\r\nbegin\r\n  AJob := JobPool.Pop;\r\n  AJob.Group := Self;\r\n  AJob.WorkerProc := AProc;\r\n  AJob.Data := AData;\r\n  AJob.SetValue(JOB_RUN_ONCE, True);\r\n  AJob.SetValue(JOB_GROUPED, True);\r\n  AJob.SetValue(JOB_IN_MAINTHREAD, AInMainThread);\r\n  InitJobFreeType(FOwner, AJob, AData, AFreeType);\r\n  FLocker.Enter;\r\n  try\r\n    FWaitResult := wrIOCompletion;\r\n    if FPrepareCount &gt; 0 then begin \/\/ \u6b63\u5728\u6dfb\u52a0\u9879\u76ee\uff0c\u52a0\u5230\u5217\u8868\u4e2d\uff0c\u7b49\u5f85Run\r\n      FItems.Add(AJob);\r\n      Result := True;\r\n    end else begin\r\n      if ByOrder then begin \/\/ \u6309\u987a\u5e8f\r\n        Result := True;\r\n        FItems.Add(AJob);\r\n        if FItems.Count = 0 then\r\n          Result := FOwner.Post(AJob);\r\n      end else begin\r\n        Result := FOwner.Post(AJob);\r\n        if Result then\r\n          FItems.Add(AJob);\r\n      end;\r\n    end;\r\n  finally\r\n    FLocker.Leave;\r\n  end;\r\nend;\r\n\r\nprocedure TJobGroup.Cancel;\r\nvar\r\n  i: Integer;\r\n  AJobs: TSimpleJobs;\r\n  AJob, APrior, ANext: PJob;\r\n  AWaitParam: TWorkerWaitParam;\r\nbegin\r\n  FLocker.Enter;\r\n  try\r\n    if FByOrder then begin\r\n      for i := 0 to FItems.Count - 1 do begin\r\n        AJob := FItems[i];\r\n        if AJob.PopTime = 0 then\r\n          FOwner.FreeJob(AJob);\r\n      end;\r\n    end;\r\n    FItems.Clear;\r\n  finally\r\n    FLocker.Leave;\r\n  end;\r\n  \/\/ \u4eceSimpleJobs\u91cc\u6e05\u9664\u5173\u8054\u7684\u5168\u90e8\u4f5c\u4e1a\r\n  AJobs := FOwner.FSimpleJobs;\r\n  AJobs.FLocker.Enter;\r\n  try\r\n    AJob := AJobs.FFirst;\r\n    APrior := nil;\r\n    while AJob &lt;&gt; nil do begin\r\n      ANext := AJob.Next;\r\n      if AJob.IsGrouped and (AJob.Group = Self) then begin\r\n        if APrior = nil then\r\n          AJobs.FFirst := AJob.Next\r\n        else\r\n          APrior.Next := AJob.Next;\r\n        AJob.Next := nil;\r\n        FOwner.FreeJob(AJob);\r\n        if AJob = AJobs.FLast then\r\n          AJobs.FLast := nil;\r\n      end else\r\n        APrior := AJob;\r\n      AJob := ANext;\r\n    end;\r\n  finally\r\n    AJobs.FLocker.Leave;\r\n  end;\r\n  AWaitParam.WaitType := 3;\r\n  AWaitParam.Group := Self;\r\n  FOwner.WaitRunningDone(AWaitParam);\r\nend;\r\n\r\nconstructor TJobGroup.Create(AOwner: TYXDWorkers; AByOrder: Boolean);\r\nbegin\r\n  if Assigned(AOwner) then\r\n    FOwner := AOwner\r\n  else\r\n    FOwner := Workers;\r\n  if (not Assigned(FOwner)) then\r\n    raise Exception.Create(SNotInitWorkers);\r\n  FEvent := TEvent.Create(nil, False, False, '');\r\n  FLocker := TSimpleLock.Create;\r\n  FByOrder := AByOrder;\r\n  FItems := TJobItemList.Create;\r\nend;\r\n\r\nconstructor TJobGroup.Create(AByOrder: Boolean);\r\nbegin\r\n  Create(nil, AByOrder);\r\nend;\r\n\r\ndestructor TJobGroup.Destroy;\r\nvar\r\n  i: Integer;\r\nbegin\r\n  Cancel;\r\n  FOwner.Clear(Self, -1);\r\n  FLocker.Enter;\r\n  try\r\n    if FItems.Count &gt; 0 then begin\r\n      FWaitResult := wrAbandoned;\r\n      FEvent.SetEvent;\r\n      for i := 0 to FItems.Count - 1 do begin\r\n        if PJob(FItems[i]).PushTime &lt;&gt; 0 then\r\n          JobPool.Push(FItems[i]);\r\n      end;\r\n      FItems.Clear;\r\n    end;\r\n  finally\r\n    FLocker.Leave;\r\n  end;\r\n  FreeAndNil(FLocker);\r\n  FreeAndNil(FEvent);\r\n  FreeAndNil(FItems);\r\n  inherited;\r\nend;\r\n\r\nprocedure TJobGroup.DoAfterDone;\r\nbegin\r\n  if Assigned(FAfterDone) then begin\r\n    try\r\n      FAfterDone(Self);\r\n    except\r\n      if Assigned(FOwner.FOnErrorNotify) then\r\n        FOwner.FOnErrorNotify(nil, Exception(ExceptObject), 'TJobGroup.DoAfterDone');\r\n    end;\r\n  end;\r\nend;\r\n\r\nprocedure TJobGroup.DoJobExecuted(AJob: PJob);\r\nvar\r\n  i: Integer;\r\n  AIsDone: Boolean;\r\nbegin\r\n  if FWaitResult = wrIOCompletion then begin\r\n    AIsDone := False;\r\n    FLocker.Enter;\r\n    try\r\n      i := FItems.IndexOf(AJob);\r\n      if i &lt;&gt; -1 then begin\r\n        FItems.Delete(i);\r\n        if FItems.Count = 0 then begin\r\n          FWaitResult := wrSignaled;\r\n          FEvent.SetEvent;\r\n          AIsDone := true;\r\n        end else if ByOrder then begin\r\n          if not FOwner.Post(FItems[0]) then begin\r\n            FWaitResult := wrAbandoned;\r\n            FEvent.SetEvent;\r\n          end;\r\n        end;\r\n      end;\r\n    finally\r\n      FLocker.Leave;\r\n    end;\r\n    if AIsDone then\r\n      DoAfterDone;\r\n  end;\r\nend;\r\n\r\nprocedure TJobGroup.DoJobsTimeout(AJob: PJob);\r\nbegin\r\n  FTimeoutCheck := False;\r\n  Cancel;\r\n  if FWaitResult = wrIOCompletion then begin\r\n    FWaitResult := wrTimeout;\r\n    FEvent.SetEvent;\r\n    DoAfterDone;\r\n  end;\r\nend;\r\n\r\nfunction TJobGroup.MsgWaitFor(ATimeout: Cardinal): TWaitResult;\r\nvar\r\n  AEndTime: Int64;\r\nbegin\r\n  if GetCurrentThreadId &lt;&gt; MainThreadId then\r\n    Result := WaitFor(ATimeout)\r\n  else begin\r\n    Result := FWaitResult;\r\n    FLocker.Enter;\r\n    try\r\n      if FItems.Count = 0 then\r\n        Result := wrSignaled;\r\n    finally\r\n      FLocker.Leave;\r\n    end;\r\n    if Result = wrIOCompletion then begin\r\n      AEndTime := GetTimestamp + ATimeout * 10;\r\n      while GetTimestamp &lt; AEndTime do begin\r\n        \/\/ \u6bcf\u969410\u6beb\u79d2\u68c0\u67e5\u4e00\u4e0b\u662f\u5426\u6709\u6d88\u606f\u9700\u8981\u5904\u7406\uff0c\u6709\u5219\u5904\u7406\uff0c\u65e0\u5219\u8fdb\u5165\u4e0b\u4e00\u4e2a\u7b49\u5f85\r\n        if FEvent.WaitFor(10) = wrSignaled then begin\r\n          Result := FWaitResult;\r\n          Break;\r\n        end else begin\r\n          \/\/ \u5982\u679c\u662f\u5728\u4e3b\u7ebf\u7a0b\u4e2d\u6e05\u7406\uff0c\u7531\u4e8e\u4f5c\u4e1a\u53ef\u80fd\u5728\u4e3b\u7ebf\u7a0b\u6267\u884c\uff0c\u53ef\u80fd\u5df2\u7ecf\u6295\u5bc4\u5c1a\u672a\u6267\u884c\uff0c\u6240\u4ee5\u5fc5\u9700\u8ba9\u5176\u80fd\u591f\u6267\u884c\r\n          {$IFDEF NEXTGEN}\r\n          fmx.Forms.Application.ProcessMessages;\r\n          {$ELSE}\r\n          Forms.Application.ProcessMessages;\r\n          {$ENDIF}\r\n        end;\r\n      end;\r\n      if Result = wrIOCompletion then begin\r\n        Cancel;\r\n        if Result = wrIOCompletion then\r\n          Result := wrTimeout;\r\n      end;\r\n      if FTimeoutCheck then\r\n        FOwner.Clear;\r\n      DoAfterDone;\r\n    end;\r\n  end;\r\nend;\r\n\r\nprocedure TJobGroup.Prepare;\r\nbegin\r\n  AtomicIncrement(FPrepareCount);\r\nend;\r\n\r\nprocedure TJobGroup.Run(ATimeout: Cardinal = INFINITE);\r\nvar\r\n  i: Integer;\r\nbegin\r\n  if AtomicDecrement(FPrepareCount) = 0 then begin\r\n    if ATimeout &lt;&gt; INFINITE then begin\r\n      FTimeoutCheck := True;\r\n      FOwner.Post(DoJobsTimeout, nil, False, ATimeout);\r\n    end;\r\n    FLocker.Enter;\r\n    try\r\n      if FItems.Count = 0 then\r\n        FWaitResult := wrSignaled\r\n      else begin\r\n        FWaitResult := wrIOCompletion;\r\n        if ByOrder then begin\r\n          if not FOwner.Post(FItems[0]) then\r\n            FWaitResult := wrAbandoned;\r\n        end else begin\r\n          for i := 0 to FItems.Count - 1 do begin\r\n            if not FOwner.Post(FItems[i]) then begin\r\n              FWaitResult := wrAbandoned;\r\n              Break;\r\n            end;\r\n          end;\r\n        end;\r\n      end;\r\n    finally\r\n      FLocker.Leave;\r\n    end;\r\n    if FWaitResult &lt;&gt; wrIOCompletion then\r\n      DoAfterDone;\r\n  end;\r\nend;\r\n\r\nfunction TJobGroup.WaitFor(ATimeout: Cardinal): TWaitResult;\r\nbegin\r\n  Result := FWaitResult;\r\n  FLocker.Enter;\r\n  try\r\n    if FItems.Count = 0 then\r\n      Result := wrSignaled;\r\n  finally\r\n    FLocker.Leave;\r\n  end;\r\n  if Result = wrIOCompletion then begin\r\n    if FEvent.WaitFor(ATimeout) = wrSignaled then\r\n      Result := FWaitResult\r\n    else\r\n      Result := wrTimeout;\r\n  end;\r\n  if FTimeoutCheck then\r\n    FOwner.Clear;\r\n  DoAfterDone;\r\nend;\r\n\r\ninitialization\r\n  _CPUCount := GetCPUCount;\r\n  {$IFNDEF NEXTGEN}\r\n  GetTickCount64 := GetProcAddress(GetModuleHandle(kernel32), 'GetTickCount64');\r\n  if not QueryPerformanceFrequency(_PerfFreq) then\r\n    _PerfFreq := -1;\r\n  {$ELSE}\r\n    _Watch := TStopWatch.Create;\r\n    _Watch.Start;\r\n  {$ENDIF}\r\n\r\n  JobPool := TJobPool.Create(1024);\r\n  \/\/Workers := TYXDWorkers.Create;\r\n\r\nfinalization\r\n  if Assigned(Workers) then FreeAndNil(Workers);\r\n  if Assigned(JobPool) then FreeAndNil(JobPool);\r\n\r\nend.\r\n\r\n<\/pre>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u8bf4\u660e &#8212;&#8212;&#038;#8 [&hellip;]<\/p>\n","protected":false},"author":3,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"default","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"categories":[8,6],"tags":[34,35],"class_list":["post-132","post","type-post","status-publish","format-standard","hentry","category-delphi","category-qworker","tag-yxdworker","tag-35"],"views":13589,"_links":{"self":[{"href":"https:\/\/blog.qdac.cc\/index.php?rest_route=\/wp\/v2\/posts\/132","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.qdac.cc\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.qdac.cc\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.qdac.cc\/index.php?rest_route=\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.qdac.cc\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=132"}],"version-history":[{"count":3,"href":"https:\/\/blog.qdac.cc\/index.php?rest_route=\/wp\/v2\/posts\/132\/revisions"}],"predecessor-version":[{"id":135,"href":"https:\/\/blog.qdac.cc\/index.php?rest_route=\/wp\/v2\/posts\/132\/revisions\/135"}],"wp:attachment":[{"href":"https:\/\/blog.qdac.cc\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=132"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.qdac.cc\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=132"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.qdac.cc\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=132"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}