Node.js 多线程完全指南总结

2020-06-17 06:09:52易采站长站整理
setInterval
,代码在这里,但因为它对线程什么都没做(我们重用
setTimeout
的代码),所以我决定不在这里进行解释。

我已经创建了一个短小的测试代码,目的是检查这种方法与原生方法的不同之处。你可以在这里找到代码。这些是结果:


native setTimeout { ms: 7004, averageCPUCost: 0.1416 }
worker setTimeout { ms: 7046, averageCPUCost: 0.308 }

我们可以看到

setTimeout
有一点延迟 – 大约40ms – 这时 worker 被创建时的消耗。平均 CPU 成本也略高,但没什么难以忍受的(CPU 成本是整个过程持续时间内 CPU 使用率的平均值)。

如果我们可以重用 worker,就能够降低延迟和 CPU 使用率,这就是要实现工作池的原因。

实现工作池

如上所述,工作池是给定数量的被事先创建的 worker,他们保持空闲并监听

message
事件。一旦
message
事件被触发,他们就会开始工作并发回结果。

为了更好地描述我们将要做的事情,下面我们来创建一个由八个 thread worker 组成的工作池:


const pool = new WorkerPool(path.join(__dirname, './test-worker.js'), 8);

如果你熟悉限制并发操作,那么你在这里看到的逻辑几乎相同,只是一个不同的用例。

如上面的代码片段所示,我们把指向 worker 的路径和要生成的 worker 数量传给了

WorkerPool
的构造函数。


export class WorkerPool<T, N> {
private queue: QueueItem<T, N>[] = [];
private workersById: { [key: number]: Worker } = {};
private activeWorkersById: { [key: number]: boolean } = {};

public constructor(public workerPath: string, public numberOfThreads: number) {
this.init();
}
}

这里还有其他一些属性,如

workersById
activeWorkersById
,我们可以分别保存现有的 worker 和当前正在运行的 worker 的 ID。还有
queue
,我们可以使用以下结构来保存对象:


type QueueCallback<N> = (err: any, result?: N) => void;

interface QueueItem<T, N> {
callback: QueueCallback<N>;
getData: () => T;
}

callback
只是默认的节点回调,第一个参数是错误,第二个参数是可能的结果。
getData