加入收藏 | 设为首页 | 会员中心 | 我要投稿 核心网 (https://www.hxwgxz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 建站 > 正文

Node.js多线程完全指南

发布时间:2019-03-29 23:34:32 所属栏目:建站 来源:疯狂的技术宅
导读:很多人都想知道单线程的 Node.js 怎么能与多线程后端竞争。考虑到其所谓的单线程特性,许多大公司选择 Node 作为其后端似乎违反直觉。要想知道原因,必须理解其单线程的真正含义。 JavaScript 的设计非常适合在网上做比较简单的事情,比如验证表单,或者说

在工作线程中:

  1. import { workerData } from 'worker_threads';  
  2. console.log(workerData.property); 

parentPort

前面提到的 MessagePort 实例,用于与父线程通信。

threadId

分配给 worker 的唯一标识符。

现在我们知道了技术细节,接下来实现一些东西并在实践中检验学到的知识。

实现 setTimeout

setTimeout 是一个无限循环,顾名思义,用来检测程序运行时间是否超时。它在循环中检查起始时间与给定毫秒数之和是否小于实际日期。

  1. import { parentPort, workerData } from 'worker_threads';  
  2. const time = Date.now();  
  3. while (true) {  
  4.     if (time + workerData.time <= Date.now()) {  
  5.         parentPort.postMessage({});  
  6.         break;  
  7.     }  

这个特定的实现产生一个线程,然后执行它的代码,最后在完成后退出。

接下来实现使用这个 worker 的代码。首先创建一个状态,用它来跟踪生成的 worker:

  1. const timeoutState: { [key: string]: Worker } = {}; 

然后时负责创建 worker 并将其保存到状态的函数:

  1. export function setTimeout(callback: (err: any) => any, time: number) {  
  2.  const id = uuidv4();  
  3.  const worker = runWorker(  
  4.    path.join(__dirname, './timeout-worker.js'),  
  5.    (err) => {  
  6.      if (!timeoutState[id]) {  
  7.        return null;  
  8.      }  
  9.      timeoutState[id] = null;  
  10.      if (err) {  
  11.        return callback(err);  
  12.      }  
  13.      callback(null);  
  14.    },  
  15.    {  
  16.      time,  
  17.    },  
  18.  );  
  19.  timeoutState[id] = worker;  
  20.  return id;  

首先,我们使用 UUID 包为 worker 创建一个唯一的标识符,然后用先前定义的函数 runWorker 来获取 worker。我们还向 worker 传入一个回调函数,一旦 worker 发送了数据就会被触发。最后,把 worker 保存在状态中并返回 id。

在回调函数中,我们必须检查该 worker 是否仍然存在于该状态中,因为有可能会 cancelTimeout(),这将会把它删除。如果确实存在,就把它从状态中删除,并调用传给 setTimeout 函数的 callback。

cancelTimeout 函数使用 .terminate() 方法强制 worker 退出,并从该状态中删除该这个worker:

  1. export function cancelTimeout(id: string) {  
  2.  if (timeoutState[id]) {  
  3.    timeoutState[id].terminate();  
  4.    timeoutState[id] = undefined;  
  5.    return true;  
  6.  }  
  7.  return false;  

如果你有兴趣,我也实现了 setInterval,代码在这里,但因为它对线程什么都没做(我们重用setTimeout的代码),所以我决定不在这里进行解释。

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

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

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

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

实现工作池

如上所述,工作池是给定数量的被事先创建的 worker,他们保持空闲并监听 message 事件。一旦 message 事件被触发,他们就会开始工作并发回结果。

(编辑:核心网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

热点阅读