我知道node.js是单线程,异步,无阻塞的I / O。我已经读了很多。例如,PHP每个请求使用一个线程,但是节点仅对所有线程使用一个线程。
假设有三个请求a,b,c同时到达node.js服务器。这些请求中的三个需要大型阻止操作,例如,它们都希望读取相同的大文件。
然后,如何将请求排队,将按什么顺序执行阻塞操作,以及按什么顺序分派响应?当然使用多少个线程?
请告诉我三个请求从请求到响应的顺序。
这是您三个请求的一系列事件的描述:
因此,即使实际上实际上只有一个请求同时在执行,多个请求也可以同时处于“处理中”或“运行中”状态。有时这称为协作多任务,而不是通过具有多个本机线程的“抢先式”多任务,系统可以随时在这些线程之间自由切换,给定的Javascript线程运行直到返回系统,然后再返回,只有这样,才能开始运行另一段Javascript。因为一段Javascript可以启动非阻塞异步操作,所以Javascript线程可以在其异步操作仍处于挂起状态时返回到系统(使其他Javascript片段可以运行)。这些操作完成后,
单螺纹
这里的关键点是,给定的Javascript线程将一直运行,直到返回系统为止。如果在执行过程中启动了一些异步操作(例如文件I / O或网络连接),那么当这些事件结束时,它们会将一个事件放入事件队列中,并且当JS引擎完成运行任何事件之前,它,该事件将得到处理,并将导致调用回调,并且该回调将轮流执行。
与多线程模型相比,这种单线程性质极大地简化了并发的处理方式。在每个请求都启动其自己的线程的完全多线程环境中,所有希望共享的数据,甚至是一个简单的变量,都必须遵守竞争条件,并且必须使用互斥体进行保护,然后任何人才能读取它。
在Javascript中,因为没有多个请求的并发执行,所以对于简单的共享变量访问不需要互斥体。从定义上看,一段Javascript正在读取一个变量,此时没有其他Javascript在运行(单线程)。
Node.js确实使用线程
注意的一种技术区别是,只有Javascript的执行是单线程的。node.js内部构件确实将线程本身用于某些方面。例如,异步文件I / O实际上使用本机线程。网络I / O实际上并不使用线程(它使用本机事件驱动的网络)。
但是,在node.js内部使用线程不会直接影响Javascript执行。一次仅执行一个Java线程。
比赛条件
启动异步操作时,在修改状态的过程中仍可能存在争用条件,但是这种方法比多线程环境中的方法少见,而且更容易识别和保护这些情况。作为可能存在的竞争条件的示例,我有一个简单的服务器,该服务器使用间隔计时器每10秒从多个温度探测器读取一次读数。它从所有这些温度读数中收集数据,并且每小时将其写出到磁盘。它使用异步I / O将数据写入磁盘。但是,由于使用了许多不同的异步文件I / O操作将数据写入磁盘,因此间隔计时器可能会在某些异步文件I / O操作之间触发,从而导致服务器所在的数据写入磁盘的中间要修改。这很糟糕,可能导致写入不一致的数据。在一个简单的世界中,可以通过在开始将所有数据复制到磁盘之前对其进行复制来避免这种情况,因此,如果在将数据写入磁盘时出现新的温度读数,则该复制将不会受到影响,并且代码仍将一组一致的数据写入磁盘。但是,对于此服务器,数据可能很大,而服务器上的内存却很小(这是Raspberry Pi服务器),因此在内存中复制所有数据是不切实际的。副本将不受影响,并且代码仍将一致的数据集写入磁盘。但是,对于此服务器,数据可能很大,而服务器上的内存却很小(这是Raspberry Pi服务器),因此在内存中复制所有数据是不切实际的。副本将不受影响,并且代码仍将一致的数据集写入磁盘。但是,对于此服务器,数据可能很大,而服务器上的内存却很小(这是Raspberry Pi服务器),因此在内存中复制所有数据是不切实际的。
因此,可以通过在将数据写入磁盘的过程中设置一个标志,然后在将数据写入磁盘后清除该标志来解决该问题。如果在设置此标志的同时触发间隔计时器,则会将新数据放入单独的队列中,并且不会修改正在写入磁盘的核心数据。将数据写入磁盘后,它将检查队列并将发现的所有温度数据添加到内存中的温度数据中。将保留正在写入磁盘的过程的完整性。每当此“竞赛条件”被击中并且由于该原因数据进入队列时,我的服务器都会记录一个事件。而且,瞧,它确实每隔一段时间就会发生一次,并且保存数据完整性的代码也起作用了。