@tachybase/module-worker-thread 0.23.58 → 1.0.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- (function(t,e){typeof exports=="object"&&typeof module!="undefined"?e(exports,require("@tachybase/client"),require("react/jsx-runtime"),require("@tachybase/schema"),require("antd")):typeof define=="function"&&define.amd?define(["exports","@tachybase/client","react/jsx-runtime","@tachybase/schema","antd"],e):(t=typeof globalThis!="undefined"?globalThis:t||self,e(t["@tachybase/module-worker-thread"]={},t["@tachybase/client"],t.jsxRuntime,t["@tachybase/schema"],t.antd))})(this,function(t,e,r,h,u){"use strict";var T=Object.defineProperty;var x=Object.getOwnPropertySymbols;var F=Object.prototype.hasOwnProperty,S=Object.prototype.propertyIsEnumerable;var b=(t,e,r)=>e in t?T(t,e,{enumerable:!0,configurable:!0,writable:!0,value:r}):t[e]=r,A=(t,e)=>{for(var r in e||(e={}))F.call(e,r)&&b(t,r,e[r]);if(x)for(var r of x(e))S.call(e,r)&&b(t,r,e[r]);return t};var c=(t,e,r)=>new Promise((h,u)=>{var p=n=>{try{o(r.next(n))}catch(l){u(l)}},d=n=>{try{o(r.throw(n))}catch(l){u(l)}},o=n=>n.done?h(n.value):Promise.resolve(n.value).then(p,d);o((r=r.apply(t,e)).next())});const p="worker-thread";function d(){return{t:(i,a={})=>e.i18n.t(i,A({ns:p},a))}}const o=s=>`{{t("${s}", { ns: ["${p}", "client"] })}}`,n=()=>{const s=e.useAPIClient();return{run(){return c(this,null,function*(){var a;(a=s.service("workerForm"))==null||a.refresh()})}}},l=s=>e.useRequest({url:"worker_thread:info"},s).data,k=()=>{const{setVisible:s}=e.useActionContext(),i=h.useForm(),{message:a}=u.App.useApp(),m=e.useAPIClient(),{t:C}=d();return{run(){return c(this,null,function*(){yield i.submit(),yield m.request({url:"worker_thread:preset",method:"post",data:{count:i.values.preset}}),a.success(C("Saved successfully")),s(!1)})}}},w=()=>{const{setVisible:s}=e.useActionContext(),{message:i}=u.App.useApp(),a=e.useAPIClient(),{t:m}=d();return{run(){return c(this,null,function*(){const I=i.loading(m("Restarting all..."),0);try{yield a.request({url:"worker_thread:restartAllForcely",method:"post",data:{}}),i.success(m("Restart all successfully"))}catch(g){i.error(g.message)}finally{I()}s(!1)})}}},v={type:"object",properties:{workerForm:{"x-decorator":"Form","x-uid":"workerForm","x-decorator-props":{useValues:"{{ useWorkerInfo }}"},"x-component":"div",type:"void",title:'{{t("Worker thread info")}}',properties:{preset:{type:"number",title:o("Preset count"),minimum:0,"x-decorator":"FormItem","x-component":"InputNumber","x-component-props":{precision:0},required:!0},current:{type:"digit",title:o("Current count"),"x-decorator":"FormItem","x-component":"InputNumber","x-disabled":!0},busy:{type:"digit",title:o("Busy count"),"x-decorator":"FormItem","x-component":"InputNumber","x-disabled":!0},env:{type:"digit",title:o("Environment count"),"x-decorator":"FormItem","x-component":"InputNumber","x-disabled":!0},footer1:{type:"void","x-component":"ActionBar","x-component-props":{layout:"one-column"},properties:{submit:{title:o("Submit"),"x-component":"Action","x-component-props":{type:"primary",htmlType:"submit",useAction:"{{ resetWorkerCount }}"}},refresh:{title:o("Refresh"),"x-component":"Action","x-component-props":{type:"default",useAction:"{{ useRefresh }}"}},restartAllForcely:{title:o("Restart all forcely"),"x-component":"Action","x-component-props":{type:"default",useAction:w,confirm:{title:o("Restart all forcely"),content:o("Are you sure you want to restart all worker threads? All worker threads that are currently processing main thread tasks will be immediately terminated")}}}}},helpText1:{type:"void","x-component":"div","x-component-props":{style:{color:"grey",fontSize:"12px",marginTop:"10px"}},"x-content":o("If the current preset count is less than the number of worker threads, it will wait for the worker threads to be idle before exiting")},helpText2:{type:"void","x-component":"div","x-component-props":{style:{color:"red",fontSize:"12px",marginTop:"10px"}},"x-content":o("The modification will not be refreshed immediately, please wait patiently for about 10 seconds, or click the refresh button to view the latest data")}}}}},y=()=>r.jsx(u.Card,{bordered:!1,children:r.jsx(e.SchemaComponent,{scope:{resetWorkerCount:k,useWorkerInfo:l,useRefresh:n},schema:v})});class f extends e.Plugin{afterAdd(){return c(this,null,function*(){})}beforeLoad(){return c(this,null,function*(){})}load(){return c(this,null,function*(){this.app.addComponents({WorkerInfoPane:y}),this.app.systemSettingsManager.add("system-services."+p,{title:this.t("Worker thread"),icon:"ControlOutlined",Component:y,aclSnippet:`pm.system-services.${p}`,sort:10})})}}t.ModuleWorkerThreadClient=f,t.default=f,Object.defineProperties(t,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
1
+ (function(t,e){typeof exports=="object"&&typeof module!="undefined"?e(exports,require("@tachybase/client"),require("react/jsx-runtime"),require("@tachybase/schema"),require("antd")):typeof define=="function"&&define.amd?define(["exports","@tachybase/client","react/jsx-runtime","@tachybase/schema","antd"],e):(t=typeof globalThis!="undefined"?globalThis:t||self,e(t["@tachybase/module-worker-thread"]={},t["@tachybase/client"],t.jsxRuntime,t["@tachybase/schema"],t.antd))})(this,function(t,e,r,h,u){"use strict";var F=Object.defineProperty;var f=Object.getOwnPropertySymbols;var T=Object.prototype.hasOwnProperty,S=Object.prototype.propertyIsEnumerable;var b=(t,e,r)=>e in t?F(t,e,{enumerable:!0,configurable:!0,writable:!0,value:r}):t[e]=r,A=(t,e)=>{for(var r in e||(e={}))T.call(e,r)&&b(t,r,e[r]);if(f)for(var r of f(e))S.call(e,r)&&b(t,r,e[r]);return t};var c=(t,e,r)=>new Promise((h,u)=>{var p=s=>{try{o(r.next(s))}catch(l){u(l)}},d=s=>{try{o(r.throw(s))}catch(l){u(l)}},o=s=>s.done?h(s.value):Promise.resolve(s.value).then(p,d);o((r=r.apply(t,e)).next())});const p="worker-thread";function d(){return{t:(i,a={})=>e.i18n.t(i,A({ns:p},a))}}const o=n=>`{{t("${n}", { ns: ["${p}", "client"] })}}`,s=()=>{const n=e.useAPIClient();return{run(){return c(this,null,function*(){var a;(a=n.service("workerForm"))==null||a.refresh()})}}},l=n=>e.useRequest({url:"worker_thread:info"},n).data,k=()=>{const{setVisible:n}=e.useActionContext(),i=h.useForm(),{message:a}=u.App.useApp(),m=e.useAPIClient(),{t:C}=d();return{run(){return c(this,null,function*(){yield i.submit(),yield m.request({url:"worker_thread:preset",method:"post",data:{count:i.values.preset}}),a.success(C("Saved successfully")),n(!1)})}}},w=()=>{const{setVisible:n}=e.useActionContext(),{message:i}=u.App.useApp(),a=e.useAPIClient(),{t:m}=d();return{run(){return c(this,null,function*(){const I=i.loading(m("Restarting all..."),0);try{yield a.request({url:"worker_thread:restartAllForcely",method:"post",data:{}}),i.success(m("Restart all successfully"))}catch(g){i.error(g.message)}finally{I()}n(!1)})}}},v={type:"object",properties:{workerForm:{"x-decorator":"Form","x-uid":"workerForm","x-decorator-props":{useValues:"{{ useWorkerInfo }}"},"x-component":"div",type:"void",title:'{{t("Worker thread info")}}',properties:{preset:{type:"number",title:o("Preset count"),minimum:0,"x-decorator":"FormItem","x-component":"InputNumber","x-component-props":{precision:0},required:!0},current:{type:"digit",title:o("Current count"),"x-decorator":"FormItem","x-component":"InputNumber","x-disabled":!0},busy:{type:"digit",title:o("Busy count"),"x-decorator":"FormItem","x-component":"InputNumber","x-disabled":!0},env:{type:"digit",title:o("Environment count"),"x-decorator":"FormItem","x-component":"InputNumber","x-disabled":!0},max:{type:"digit",title:o("Max count"),"x-decorator":"FormItem","x-component":"InputNumber","x-disabled":!0},footer1:{type:"void","x-component":"ActionBar","x-component-props":{layout:"one-column"},properties:{submit:{title:o("Submit"),"x-component":"Action","x-component-props":{type:"primary",htmlType:"submit",useAction:"{{ resetWorkerCount }}"}},refresh:{title:o("Refresh"),"x-component":"Action","x-component-props":{type:"default",useAction:"{{ useRefresh }}"}},restartAllForcely:{title:o("Restart all forcely"),"x-component":"Action","x-component-props":{type:"default",useAction:w,confirm:{title:o("Restart all forcely"),content:o("Are you sure you want to restart all worker threads? All worker threads that are currently processing main thread tasks will be immediately terminated")}}}}},helpText1:{type:"void","x-component":"div","x-component-props":{style:{color:"grey",fontSize:"12px",marginTop:"10px"}},"x-content":o("If the current preset count is less than the number of worker threads, it will wait for the worker threads to be idle before exiting")},helpText2:{type:"void","x-component":"div","x-component-props":{style:{color:"red",fontSize:"12px",marginTop:"10px"}},"x-content":o("The modification will not be refreshed immediately, please wait patiently for about 10 seconds, or click the refresh button to view the latest data")}}}}},y=()=>r.jsx(u.Card,{bordered:!1,children:r.jsx(e.SchemaComponent,{scope:{resetWorkerCount:k,useWorkerInfo:l,useRefresh:s},schema:v})});class x extends e.Plugin{afterAdd(){return c(this,null,function*(){})}beforeLoad(){return c(this,null,function*(){})}load(){return c(this,null,function*(){this.app.addComponents({WorkerInfoPane:y}),this.app.systemSettingsManager.add("system-services."+p,{title:this.t("Worker thread"),icon:"ControlOutlined",Component:y,aclSnippet:`pm.system-services.${p}`,sort:10})})}}t.ModuleWorkerThreadClient=x,t.default=x,Object.defineProperties(t,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
@@ -1,12 +1,12 @@
1
1
  module.exports = {
2
2
  "react": "18.3.1",
3
- "@tachybase/client": "0.23.58",
4
- "@tachybase/schema": "0.23.58",
3
+ "@tachybase/client": "1.0.18",
4
+ "@tachybase/schema": "1.0.18",
5
5
  "antd": "5.22.5",
6
- "@tachybase/server": "0.23.58",
7
- "@tachybase/logger": "0.23.58",
8
- "@tachybase/module-collection": "0.23.58",
9
- "@tachybase/module-user": "0.23.58",
10
- "@tachybase/utils": "0.23.58",
11
- "@tachybase/actions": "0.23.58"
6
+ "@tachybase/server": "1.0.18",
7
+ "@tachybase/logger": "1.0.18",
8
+ "@tachybase/module-collection": "1.0.18",
9
+ "@tachybase/module-user": "1.0.18",
10
+ "@tachybase/utils": "1.0.18",
11
+ "@tachybase/actions": "1.0.18"
12
12
  };
@@ -5,6 +5,7 @@
5
5
  "Environment count": "Environment count",
6
6
  "If the current preset count is less than the number of worker threads, it will wait for the worker threads to be idle before exiting": "If the current preset count is less than the number of worker threads, it will wait for the worker threads to be idle before exiting",
7
7
  "Invalid worker count": "Invalid worker count",
8
+ "Max count": "Max count",
8
9
  "No worker thread": "No worker thread",
9
10
  "Preset count": "Preset count",
10
11
  "Restart all forcely": "Restart all forcely",
@@ -5,6 +5,7 @@
5
5
  "Environment count": "环境变量预设数量",
6
6
  "If the current preset count is less than the number of worker threads, it will wait for the worker threads to be idle before exiting": "提交修改如果当前预设数量小于工作线程数量,则会等待工作线程空闲后再退出",
7
7
  "Invalid worker count": "无效的线程数",
8
+ "Max count": "最大数量",
8
9
  "No worker thread": "当前没有工作线程",
9
10
  "Preset count": "预设数量",
10
11
  "Restart all forcely": "强制重启所有",
@@ -4,3 +4,4 @@ export declare const WORKER_TIMEOUT: number;
4
4
  export declare const WORKER_ERROR_RETRY: number;
5
5
  export declare const WORKER_COUNT_SUB: number;
6
6
  export declare const WORKER_FILE = "./worker";
7
+ export declare const WORKER_COUNT_MAX_SUB: number;
@@ -19,6 +19,7 @@ var constants_exports = {};
19
19
  __export(constants_exports, {
20
20
  WORKER_COUNT: () => WORKER_COUNT,
21
21
  WORKER_COUNT_MAX: () => WORKER_COUNT_MAX,
22
+ WORKER_COUNT_MAX_SUB: () => WORKER_COUNT_MAX_SUB,
22
23
  WORKER_COUNT_SUB: () => WORKER_COUNT_SUB,
23
24
  WORKER_ERROR_RETRY: () => WORKER_ERROR_RETRY,
24
25
  WORKER_FILE: () => WORKER_FILE,
@@ -31,10 +32,12 @@ const WORKER_TIMEOUT = process.env.WORKER_TIMEOUT ? +process.env.WORKER_TIMEOUT
31
32
  const WORKER_ERROR_RETRY = process.env.WORKER_ERROR_RETRY ? +process.env.WORKER_ERROR_RETRY : 3;
32
33
  const WORKER_COUNT_SUB = process.env.WORKER_COUNT_SUB ? +process.env.WORKER_COUNT_SUB : 0;
33
34
  const WORKER_FILE = "./worker";
35
+ const WORKER_COUNT_MAX_SUB = process.env.WORKER_COUNT_MAX_SUB ? +process.env.WORKER_COUNT_MAX_SUB : 1;
34
36
  // Annotate the CommonJS export names for ESM import in node:
35
37
  0 && (module.exports = {
36
38
  WORKER_COUNT,
37
39
  WORKER_COUNT_MAX,
40
+ WORKER_COUNT_MAX_SUB,
38
41
  WORKER_COUNT_SUB,
39
42
  WORKER_ERROR_RETRY,
40
43
  WORKER_FILE,
@@ -68,7 +68,8 @@ __export(plugin_exports, {
68
68
  module.exports = __toCommonJS(plugin_exports);
69
69
  var import_worker_threads = require("worker_threads");
70
70
  var import_server = require("@tachybase/server");
71
- var import_constants = require("./constants");
71
+ var import_constants = require("../constants");
72
+ var import_constants2 = require("./constants");
72
73
  var import_workerManager = require("./workerManager");
73
74
  var import_workerWebController = require("./workerWebController");
74
75
  var _ModuleWorkerThreadServer_decorators, _init, _a;
@@ -87,7 +88,7 @@ class ModuleWorkerThreadServer extends (_a = import_server.Plugin) {
87
88
  if (!import_worker_threads.isMainThread) {
88
89
  return;
89
90
  }
90
- const workerCount = this.app.name === "main" ? import_constants.WORKER_COUNT : import_constants.WORKER_COUNT_SUB;
91
+ const workerCount = this.app.name === "main" ? import_constants2.WORKER_COUNT : import_constants2.WORKER_COUNT_SUB;
91
92
  this.app.on("afterStart", async () => {
92
93
  if (this.app.worker) {
93
94
  await this.app.worker.clear();
@@ -105,7 +106,7 @@ class ModuleWorkerThreadServer extends (_a = import_server.Plugin) {
105
106
  }
106
107
  });
107
108
  this.app.acl.registerSnippet({
108
- name: `pm.system-services.${this.name}`,
109
+ name: `pm.system-services.${import_constants.NAMESPACE}`,
109
110
  actions: ["worker_thread:*"]
110
111
  });
111
112
  }
@@ -93,12 +93,20 @@ const _WorkerManager = class _WorkerManager {
93
93
  await Promise.all(Array.from({ length: this.workerNum }).map(() => this.addWorker()));
94
94
  }
95
95
  async addWorker() {
96
+ const maxMemory = process.env.WORKER_MAX_MEMORY || 4096;
97
+ const maxOldSpaceSize = `--max-old-space-size=${maxMemory}`;
96
98
  let worker;
97
99
  if (this.isProd) {
98
100
  worker = new import_worker_threads.Worker(import_path.default.resolve(__dirname, `${import_constants.WORKER_FILE}.js`), {
99
101
  workerData: {
100
102
  appName: this.app.name,
101
103
  databaseOptions: this.databaseOptions
104
+ },
105
+ env: {
106
+ ...process.env,
107
+ ...{
108
+ NODE_OPTIONS: maxOldSpaceSize
109
+ }
102
110
  }
103
111
  });
104
112
  } else {
@@ -107,6 +115,12 @@ const _WorkerManager = class _WorkerManager {
107
115
  scriptPath: import_path.default.resolve(__dirname, `${import_constants.WORKER_FILE}.ts`),
108
116
  appName: this.app.name,
109
117
  databaseOptions: this.databaseOptions
118
+ },
119
+ env: {
120
+ ...process.env,
121
+ ...{
122
+ NODE_OPTIONS: maxOldSpaceSize
123
+ }
110
124
  }
111
125
  });
112
126
  }
@@ -323,7 +337,11 @@ const _WorkerManager = class _WorkerManager {
323
337
  worker.off("message", handleMessage);
324
338
  worker.off("error", handleError);
325
339
  worker.off("exit", handleExit);
326
- this.handleWorkerCompletion(worker, reqId, resolve, message.result);
340
+ if (message.error) {
341
+ this.handleWorkerCompletion(worker, reqId, resolve, void 0, reject, message.error);
342
+ } else {
343
+ this.handleWorkerCompletion(worker, reqId, resolve, message.result);
344
+ }
327
345
  }
328
346
  };
329
347
  handleError = (error) => {
@@ -1,6 +1,6 @@
1
1
  import { Context, Next } from '@tachybase/actions';
2
2
  export declare class WorkerWebController {
3
- info(ctx: Context, next: Next): Promise<void>;
4
- preset(ctx: Context, next: Next): Promise<void>;
5
- resetAllForcely(ctx: Context, next: Next): Promise<void>;
3
+ info(ctx: Context, next: Next): Promise<any>;
4
+ preset(ctx: Context, next: Next): Promise<any>;
5
+ restartAllForcely(ctx: Context, next: Next): Promise<any>;
6
6
  }
@@ -68,8 +68,8 @@ module.exports = __toCommonJS(workerWebController_exports);
68
68
  var import_utils = require("@tachybase/utils");
69
69
  var import_constants = require("../constants");
70
70
  var import_constants2 = require("./constants");
71
- var _resetAllForcely_dec, _preset_dec, _info_dec, _WorkerWebController_decorators, _init;
72
- _WorkerWebController_decorators = [(0, import_utils.Controller)("worker_thread")], _info_dec = [(0, import_utils.Action)("info", { acl: "blocked" })], _preset_dec = [(0, import_utils.Action)("preset", { acl: "blocked" })], _resetAllForcely_dec = [(0, import_utils.Action)("restartAllForcely", { acl: "blocked" })];
71
+ var _restartAllForcely_dec, _preset_dec, _info_dec, _WorkerWebController_decorators, _init;
72
+ _WorkerWebController_decorators = [(0, import_utils.Controller)("worker_thread")], _info_dec = [(0, import_utils.Action)("info", { acl: "private" })], _preset_dec = [(0, import_utils.Action)("preset", { acl: "private" })], _restartAllForcely_dec = [(0, import_utils.Action)("restartAllForcely", { acl: "private" })];
73
73
  class WorkerWebController {
74
74
  constructor() {
75
75
  __runInitializers(_init, 5, this);
@@ -87,13 +87,16 @@ class WorkerWebController {
87
87
  const current = app.worker.getCurrentWorkerNum();
88
88
  const busy = app.worker.getBusyWorkerNum();
89
89
  const env = app.name === "main" ? import_constants2.WORKER_COUNT : import_constants2.WORKER_COUNT_SUB;
90
+ const max = app.name === "main" ? import_constants2.WORKER_COUNT_MAX : import_constants2.WORKER_COUNT_MAX_SUB;
90
91
  ctx.body = {
91
92
  preset,
92
93
  current,
93
94
  busy,
94
- env
95
+ env,
96
+ max
95
97
  };
96
98
  }
99
+ return next();
97
100
  }
98
101
  async preset(ctx, next) {
99
102
  const { count } = ctx.action.params.values;
@@ -101,7 +104,8 @@ class WorkerWebController {
101
104
  ctx.throw(400, ctx.t("Invalid worker count", { ns: import_constants.NAMESPACE }));
102
105
  }
103
106
  const app = ctx.app;
104
- if (count > import_constants2.WORKER_COUNT_MAX) {
107
+ const countMax = app.name === "main" ? import_constants2.WORKER_COUNT_MAX : import_constants2.WORKER_COUNT_MAX_SUB;
108
+ if (count > countMax) {
105
109
  ctx.throw(400, ctx.t("Too many workers", { ns: import_constants.NAMESPACE }));
106
110
  }
107
111
  if (!app.worker) {
@@ -111,19 +115,21 @@ class WorkerWebController {
111
115
  ctx.body = {
112
116
  success: true
113
117
  };
118
+ return next();
114
119
  }
115
- async resetAllForcely(ctx, next) {
120
+ async restartAllForcely(ctx, next) {
116
121
  const app = ctx.app;
117
122
  await app.worker.restartAllForcely();
118
123
  ctx.body = {
119
124
  success: true
120
125
  };
126
+ return next();
121
127
  }
122
128
  }
123
129
  _init = __decoratorStart(null);
124
130
  __decorateElement(_init, 1, "info", _info_dec, WorkerWebController);
125
131
  __decorateElement(_init, 1, "preset", _preset_dec, WorkerWebController);
126
- __decorateElement(_init, 1, "resetAllForcely", _resetAllForcely_dec, WorkerWebController);
132
+ __decorateElement(_init, 1, "restartAllForcely", _restartAllForcely_dec, WorkerWebController);
127
133
  WorkerWebController = __decorateElement(_init, 0, "WorkerWebController", _WorkerWebController_decorators, WorkerWebController);
128
134
  __runInitializers(_init, 1, WorkerWebController);
129
135
  // Annotate the CommonJS export names for ESM import in node:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tachybase/module-worker-thread",
3
- "version": "0.23.58",
3
+ "version": "1.0.18",
4
4
  "description": "Worker thread for executing time-consuming tasks",
5
5
  "main": "dist/server/index.js",
6
6
  "dependencies": {},
@@ -9,15 +9,15 @@
9
9
  "tsx": "^4.19.2"
10
10
  },
11
11
  "peerDependencies": {
12
- "@tachybase/client": "0.23.58",
13
- "@tachybase/logger": "0.23.58",
14
- "@tachybase/actions": "0.23.58",
15
- "@tachybase/module-collection": "0.23.58",
16
- "@tachybase/module-user": "0.23.58",
17
- "@tachybase/schema": "0.23.58",
18
- "@tachybase/test": "0.23.58",
19
- "@tachybase/utils": "0.23.58",
20
- "@tachybase/server": "0.23.58"
12
+ "@tachybase/actions": "1.0.18",
13
+ "@tachybase/client": "1.0.18",
14
+ "@tachybase/logger": "1.0.18",
15
+ "@tachybase/module-collection": "1.0.18",
16
+ "@tachybase/module-user": "1.0.18",
17
+ "@tachybase/schema": "1.0.18",
18
+ "@tachybase/server": "1.0.18",
19
+ "@tachybase/test": "1.0.18",
20
+ "@tachybase/utils": "1.0.18"
21
21
  },
22
22
  "description.zh-CN": "工作线程,用于执行耗时任务",
23
23
  "displayName.zh-CN": "工作线程",