Новый модуль HeavyCalc для тяжелых вычислений в отдельном потоке
This commit is contained in:
133
server/core/HeavyCalc.js
Normal file
133
server/core/HeavyCalc.js
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
const { Worker } = require('worker_threads');
|
||||||
|
|
||||||
|
class CalcThread {
|
||||||
|
constructor() {
|
||||||
|
this.worker = null;
|
||||||
|
this.listeners = new Map();
|
||||||
|
this.requestId = 0;
|
||||||
|
|
||||||
|
this.runWorker();
|
||||||
|
}
|
||||||
|
|
||||||
|
terminate() {
|
||||||
|
if (this.worker) {
|
||||||
|
this.worker.terminate();
|
||||||
|
|
||||||
|
for (const listener of this.listeners.values()) {
|
||||||
|
listener({error: 'Worker terminated'});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.worker = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
runWorker() {
|
||||||
|
const workerProc = () => {
|
||||||
|
const { parentPort } = require('worker_threads');
|
||||||
|
|
||||||
|
const sleep = (ms) => {
|
||||||
|
return new Promise(resolve => setTimeout(resolve, ms));
|
||||||
|
};
|
||||||
|
|
||||||
|
if (parentPort) {
|
||||||
|
parentPort.on('message', async(mes) => {
|
||||||
|
let result = {};
|
||||||
|
try {
|
||||||
|
const fn = new Function(`'use strict'; return ${mes.fn}`)();
|
||||||
|
result.result = await fn(mes.args, sleep);
|
||||||
|
} catch (e) {
|
||||||
|
result = {error: e.message};
|
||||||
|
}
|
||||||
|
|
||||||
|
result.requestId = mes.requestId;
|
||||||
|
parentPort.postMessage(result);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const worker = new Worker(`const wp = ${workerProc.toString()}; wp();`, {eval: true});
|
||||||
|
|
||||||
|
worker.on('message', (mes) => {
|
||||||
|
const listener = this.listeners.get(mes.requestId);
|
||||||
|
if (listener) {
|
||||||
|
this.listeners.delete(mes.requestId);
|
||||||
|
listener(mes);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
worker.on('error', (err) => {
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
worker.on('exit', () => {
|
||||||
|
this.terminate();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.worker = worker;
|
||||||
|
}
|
||||||
|
|
||||||
|
//async
|
||||||
|
run(params) {//args, fn
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.requestId++;
|
||||||
|
|
||||||
|
this.listeners.set(this.requestId, (mes) => {
|
||||||
|
if (mes.error)
|
||||||
|
reject(new Error(mes.error));
|
||||||
|
else
|
||||||
|
resolve(mes.result);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.worker) {
|
||||||
|
this.worker.postMessage({requestId: this.requestId, args: params.args, fn: params.fn.toString()});
|
||||||
|
} else {
|
||||||
|
reject(new Error('Worker does not exist'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class HeavyCalc {
|
||||||
|
constructor(opts = {}) {
|
||||||
|
this.threads = opts.threads || 1;
|
||||||
|
this.singleton = opts.singleton || false;
|
||||||
|
this.terminated = false;
|
||||||
|
|
||||||
|
this.workers = [];
|
||||||
|
this.load = [];
|
||||||
|
for (let i = 0; i < this.threads; i++) {
|
||||||
|
const worker = new CalcThread();
|
||||||
|
this.workers.push(worker);
|
||||||
|
this.load.push(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async run(params) {
|
||||||
|
if (this.terminated || !this.workers.length)
|
||||||
|
throw new Error('All workers terminated');
|
||||||
|
|
||||||
|
//находим поток с минимальной нагрузкой
|
||||||
|
let found = 0;
|
||||||
|
for (let i = 1; i < this.load.length; i++) {
|
||||||
|
if (this.load[i] < this.load[found])
|
||||||
|
found = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.load[found]++;
|
||||||
|
return await this.workers[found].run(params);
|
||||||
|
} finally {
|
||||||
|
this.load[found]--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
terminate() {
|
||||||
|
for (let i = 0; i < this.workers.length; i++) {
|
||||||
|
this.workers[i].terminate();
|
||||||
|
}
|
||||||
|
this.workers = [];
|
||||||
|
this.load = [];
|
||||||
|
this.terminated = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = HeavyCalc;
|
||||||
Reference in New Issue
Block a user