我一生都认为Node.js和JavaScript是单线程语言。Node.js不适用于CPU密集型任务,但由于其单线程性质,因此它是轻量级的。多线程对于CPU密集型任务很有用,因为您可以将任务委派给不同的线程,但它会为竞争条件创造条件,这会变得很复杂。
然后是工作线程,告诉我节点现在可以生成名为“工作线程”的线程以通过CPU密集型任务,从而不会阻塞JavaScript堆栈。人们为什么将JavaScript称为永久定义的单线程,如果借助工作线程的力量实际上可以将其变为多线程呢?还是JavaScript确实是永久性的单线程,但是借助工作线程的强大功能,一个进程能够拥有多个JavaScript线程,而这些线程仍然表现为单线程?
Node.js使用两种线程:由事件循环处理的主线程和工作池中的多个辅助线程。
另外,我读过的这篇文章是上述声明。这听起来好像JavaScript实际上一直在使用多个不同的线程。人们为什么称JavaScript为单线程?
这听起来好像JavaScript实际上一直在使用多个不同的线程。人们为什么称JavaScript为单线程?
Node.js中的编程模型是一个单线程事件循环,可以访问异步操作,这些操作使用本机代码为某些操作(磁盘I / O,网络,计时器,某些加密操作等)实现异步行为。
另外,请记住,此编程模型不是JavaScript语言本身的产品。它是如何在事件驱动的实现中将JavaScript部署在Node.js和浏览器等流行环境中的产品。
内部存在用于执行某些异步操作(例如文件I / O或某些加密操作)的本机代码线程池的事实并没有改变编程模型是单线程事件循环的事实。线程池就是如何通过JavaScript实现耗时任务的实现以使其具有异步接口。这是一个实现细节,它不会从单线程事件循环模型中更改JavaScript编程模型。
类似地,您现在可以实际创建WorkerThreads的事实并没有真正改变主编程模型,这是因为WorkerThreads在具有单独的事件循环的单独的JavaScript VM中运行并且不共享常规变量。因此,无论是否使用WorkerThreads,您都仍然需要为事件驱动的非阻塞系统设计代码。
WorkerThreads确实允许您卸载一些费时的任务,以使它们脱离主事件循环,以使该主事件循环保持更高的响应速度,这在某些情况下是一个非常有用的选择。但是,整体模型不会改变。例如,所有联网仍然是事件驱动的且非阻塞的,异步的。因此,仅因为我们拥有WorkerThreads,这并不意味着您现在就可以像现在有时在Java中那样用JavaScript编程网络,为每个新的传入请求使用单独的线程。JavaScript模型的那部分完全没有改变。如果Node.js中有一个HTTP服务器,则它一次仍接收一个传入请求,直到该先前的传入请求将控制权返回到事件循环之前,它才开始处理下一个传入请求。
另外,您应该知道,Node.js中WorkerThreads的当前实现相当繁重。创建WorkerThread会启动一个新的JavaScript VM,初始化一个新的全局上下文,建立一个新的堆,启动一个新的垃圾收集器,分配一些内存,等等。虽然在某些情况下有用,但是这些WorkerThreads很多,比操作系统级别的线程重得多。我认为它们几乎就像迷你子进程一样,但是它们的优点是它们可以在WorkerThreads之间或主线程与WorkerThreads之间使用您无法使用实际子进程的SharedMemory。
还是JavaScript确实是永久性的单线程,但是借助工作线程的强大功能,一个进程能够拥有多个JavaScript线程,而这些线程仍然表现为单线程?
首先,JavaScript语言规范中没有固有的要求单线程的东西。单线程编程模型是如何在诸如Node.js和浏览器之类的流行编程环境中实现JavaScript语言的产物。因此,在谈论单线程性时,您应该谈论的是编程环境(例如Node.js),而不是语言本身。
在Node.js中,一个进程现在可以具有多个JavaScript线程(使用WorkerThreads)。它们独立运行,因此您可以在多个线程中同时并行运行JavaScript。为了避免线程同步的许多陷阱,WorkerThreads在单独的VM中运行,除了非常仔细地分配和控制的SharedMemory缓冲区外,不共享对其他WorkerThreads或主线程的变量的访问。通常,WorkerThreads将使用通过事件循环运行的消息传递与主线程进行通信(因此,所有JavaScript线程都必须以此方式强制执行同步级别)。
这是使用WorkerThreads的示例实现。我正在编写一个测试程序,其目的是对活动进行数十亿次模拟,并记录所有结果的统计信息,以查看结果的随机性。模拟的某些部分涉及一些加密操作,这些操作在CPU上非常耗时。在我的第一代代码中,我运行的迭代次数较少,但很显然,所需的数十亿次迭代将花费许多小时才能运行。
通过测试和测量,我能够找出代码的哪些部分使用了最多的CPU,然后创建了一个WorkerThread池(8个工作线程),可以将更多耗时的工作传递给它们,然后它们可以在其中工作。平行。这将运行模拟的总时间减少了7倍。
现在,我也可以为此使用子进程,但是它们的效率会降低,因为我需要在主线程和workerThread之间传递大型数据缓冲区(workerThread在该缓冲区中处理数据),并且这样做很多使用SharedArrayBuffer进行此操作比在父进程和子进程之间传递数据(这将涉及复制数据而不是共享数据)更有效。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句