原创文章,推荐给想了解进程,线程,io的朋友。如果你想了解worker,你首先需要了解节点是如何组成的。当一个节点进程启动时,它就是:一个进程。一个线程。事件中断。一个js引擎实例。一个node.js实例。进程:指一个全局对象,可以在任何地方访问,包含当前进程的信息。单线程:单线程意味着每单位时间在给定进程中只执行一组指令。事件骤降:这是理解Node.js最重要的概念。它使Node更加异步并且具有无锁I/O。即使js是单线程的,通过提供回调函数、promise函数和异步async/await函数等一些系统核心操作。一个JS引擎实例:这是一个用来执行js代码的计算机程序。Node.js实例:用于执行Node.js代码的计算机程序。简而言之,Node运行在单线程上,每个事件周期只存在一个进程。一次执行一个代码(非并行)。这个很重要,因为很简单,你不用考虑并发问题。之所以这样设计,是因为js本来就是为了开发客户端交互(比如页面交互,表单等),对线程没有复杂的要求。但是,就像所有事情一样,这也有其缺点:如果您有CPU密集型代码,例如在内存中来回计算的大量复杂数据,那么这可能会锁定其他需要处理的任务。比如你向服务端发起一个请求,响应这个请求的接口有cpu敏感的代码,那么它就可以锁定eventsag,防止其他请求被处理(作者:其实其他请求需要排队时间长甚至超时)。如果主事件下陷必须等待一个函数完成才能执行其他命令,则该函数被“锁定”。无锁函数可以让主事件sag从一开始就一直运行,执行完成后通知主事件sag调用回调函数。黄金法则:不要锁定事件骤降,尽量关注并避免可能导致锁定的任务,例如同步网络调用或无线无限循环。理解cpu操作和i/o操作是非常重要的。如上所述,Node中的代码不能并行执行。只是i/o是并行化的,因为它们是异步执行的。因此,workerthread(我们下面会用到这个node-specific的概念)并不能提升很多对i/o敏感的任务,因为异步i/o本身就比worker效率高很多。worker的主要任务是提高CPU敏感操作的性能。现有解决方案此外,已经有一些针对CPU敏感处理的解决方案:多进程(例如,集群API)以确保最大的CPU利用率。这种方式的好处是让每个进程都是独立的,如果一个线程出了问题,也不会影响到其他的。它们稳定且相同的api。但是,这意味着牺牲了内存共享,数据通信必须使用json(有额外的开销,性能略低)。JavaScript和Node.js永远不会是多线程的。原因如下:所以,有人可能会考虑在node.js中加入一个新的模块,让我们创建一个同步线程来解决cpu敏感处理的问题。然而,这不会发生。如果添加线程,语言的性质就会改变。不可能使用类或函数将线程添加为新功能。在支持多线程的语言(如java)中,“synchronized”等关键字可以帮助实现多线程。此外,某些数据不是原子的,这意味着如果您不同步处理它们,您最终可能会在两个线程上访问和更改值变量,并最终得到一个值变量,两个线程都对其进行一些处理。更改了无效值。比如一个简单的0.1+20.2运算,这个运算的小数点是17。因为小数点不是100%准确,如果不同步,一个整数可能会用一个worker得到一个非整数的数。Thebestsolutionistoimprovecpuperformance最好的解决方案是使用工作线程。浏览器很早就有worker的概念。使现有的结构从:一个进程,一个线程,一个事件凹陷,一个JS隐藏实例,一个Node.js。Node.js实例worker_threads模块可以使用线程实现js的并行执行。constworker=require('worker_threads');WorkerTheads从Node.10开始就可以使用了,不过一直处于实验状态。在12.11.0中成为稳定版。此解决方案意味着在一个进程中拥有多个Node.js实例。在工作线程中,一个线程可以有一些节点,这个节点不一定是父进程。在worker结束后留下一些分配的资源不是一个好习惯,这会导致内存泄漏。我们希望将node.js完全浸入其中,并赋予Node.js开箱即用的能力,然后在一个线程中创建一个新的Node.js实例。本质上,它在一个进程中的一个线程中独立运行。以下是WorkerTheads的不同之处:ArrayBuffers在线程之间传递内存。SharedArrayBuffer可以被每个线程访问,线程间共享内存。(仅限二进制数据)。原子可用,可以让你并行的进行一些处理,更高效,也可以让你在js中实现条件变量。MessagePort,用于不同线程之间的通信。可用于在不同的Worker之间传输结构数据、内存域和MessagePorts(对象)。MessageChannel表示用于在不同(工作)线程之间进行通信的异步双向通信通道。WorkerData用于传递初始数据。任何js数据的复制版本都会传递给Worker的构造函数。如果使用postMessage(),数据也会被复制。InterfaceAPIconst{worker,parentPort}=require('worker_threads'),worker类代表一个独立执行js的线程,parentPort是消息端口的实例。newWorker(filename)或newworker(code,{eval:true})是启动worker的两种方式。(传递一个文件名或代码来执行)。建议在生产中使用文件名。worker.on('message'),worker.postmessage(data)`监听信息并在不同线程之间发布数据。parentPort.on('message'),parentPort.postMessage(data),使用parentPort.postMessage()发送信息,在父线程使用worker.on('message')获取。在父线程中使用worker.postMessage(),在本线程中使用parentPort.on('message')类获取(当前线程为子线程)。示例const{Worker}=require('worker_threads');constworker=newWorker(`const{parentPort}=require('worker_threads');parentPort.once('message',message=>parentPort.postMessage({pong:message}));`,{eval:true});worker.on('message',message=>console.log(message));worker.postMessage('ping');执行:$node--experimental-workertest.js{pong:'ping'}这段代码实际上做的是使用newWorker创建一个线程,线程内部使用parentPort监听和接收一次性消息信息,并发布一个收到消息后的消息message一个猪线程。在仅支持实验性工作线程的Node版本中,您必须使用--experimental-worker命令行选项来执行代码。其他示例:const{Worker,isMainThread,parentPort,workerData}=require('worker_threads');if(isMainThread){module.exports=functionparseJSAsync(script){returnnewPromise((resolve,reject)=>{constworker=newWorker(filename,{workerData:script});worker.on('message',resolve);worker.on('error',reject);worker.on('exit',(code)=>{if(code!==0)reject(newError(`Worker停止,退出代码${code}`));});});};}else{const{parse}=require('some-js-parsing-library');constscript=workerData;parentPort.postMessage(解析(脚本));}需要依赖:Worker代表一个独立的js执行线程。isMainThead是一个布尔值,当前代码是否运行在Worker线程中。parentPortMessagePort对象,如果当前线程是生成的Worker线程,则允许与父线程通信。workerData可以传递给线程构造函数的任何js数据的副本。实战中,以上任务最好用线程池代替。否则,开销可能会超过收益。对Worker的期望(希望如此):传递本地处理的任务。(传递本机句柄)锁定检测。死锁是指一系列进程因为每个进程持有一些资源而被锁定的情况,每个线程都在等待其他线程持有的资源被释放,然后再获取。死锁检测在workerthead中更有用。更多的隔离,所以一旦一个线程受到影响,其他线程就没事了。不要对Workers抱有什么期望:不要认为Workers会让一切变得很快,在某些情况下使用线程池会更好。不要使用worker进行io并行操作。不要认为生成线程很便宜。最后:Workers有chrome开发工具,可用于监控Node.js中的worker。
