如何精确地在“现实世界”中每n毫秒调用一次函数?

时间:2018-12-01 08:49:20

标签: node.js setinterval

如果我理解正确,setInterval(() => console.log('hello world'), 1000)会将函数放置在要运行的任务队列中。但是,如果前面还有其他任务,它将无法精确地在1000毫秒或每次运行。

在单个复杂程序中,是否还可以使用node.js在现实世界中每n毫秒精确地调用某个函数?

4 个答案:

答案 0 :(得分:2)

  

但是如果它前面还有其他任务,它将无法精确地在1000毫秒或每次运行。

假设计算机正在运行某些(常用)操作系统(例如Windows,Android,Linux,MacOSX等),则您的问题实际上是operating system所特有的。我建议阅读Operating Systems: Three Easy Pieces了解更多信息。

实际上,您的计算机还有许多其他processes由其操作系统管理。其中一些可能正在运行。您的计算机可能处于被其他进程充分加载的状态,以致无法每秒精确地运行您的任务或线程。了解有关thrashing的信息。

您可能要使用一些正版real-time operating system。但是,node.js可能不会在其上运行。

  

如何准确地在“现实世界”中每n毫秒调用一次函数?

您无法可靠地做到这一点。。因为您的node.js进程(实际上是单线程,在系统线程级别上,请参见pthreads(7)jfriend00's answer)可能无法从您的操作系统中获得足够的资源(因此,如果其他进程给您的计算机带来过多的负担,node.js将会是starved,将无法按照您的意愿进行;请注意可能的{ {3}}。

在Linux上,另请参见priority inversionsshed(7)chrt(1)

答案 1 :(得分:2)

  

如果我理解正确,则setInterval(()=> console.log('hello world'),1000)会将函数放置在一些要运行的任务队列中。但是,如果前面还有其他任务,它将无法精确地在1000毫秒或每次运行。

是正确的。如果在准备好运行计时器时,node.js恰好忙于执行其他操作,它将无法在所需的时间准确运行。在运行计时器回调之前,node.js将等待其完成其他任务。您可以将node.js看作是一心一意(一次只能做一件事),并且计时器永远不会中断正在运行的现有任务。

  

在单个复杂程序中,是否还可以使用node.js在现实世界中每n毫秒精确地调用某个函数?

否,在node.js中无法做到这一点。 node.js以单线程方式运行Javascript,它是事件驱动的,不是抢占式的。所有这些都意味着您不能依赖在精确的实际时间运行的代码。

node.js的幕后情况是您为将来的特定时间设置了一个计时器。该计时器已在node.js事件循环中注册,因此每次它通过事件循环时,它将检查是否有任何暂挂计时器。但是,只有在准备触发计时器之前运行的其他代码完成运行时,它才通过事件循环。这是事件的顺序:

  • 运行一些代码
  • 将计时器设置为将来的某个时间(例如时间X)
  • 运行更多代码
  • 无事可做
  • 再运行一些代码(运行此代码时,时间X过去了-计时器运行的时间)
  • 先前的代码块完成运行,并且控制在时间X + n(应该在计时器X触发后的某个时间)返回到node.js事件循环。
  • 事件循环检查是否有任何暂挂计时器。它会找到一个计时器,并在X + n时调用其回调。

因此,大约在X时刻调用计时器的唯一方法是,在精确的X时刻,node.js没有其他事情要做。如果您的程序在做其他事情,则不能保证您的程序会在在确切的时间X处有空,以便在您希望计时器运行时准确地运行计时器。无论如何,node.js都不是实时系统。单线程且非抢占式意味着计时器可能必须等待node.js完成一些其他事情才能运行,因此无法保证计时器会准时运行。取而代之的是,它将在时间X之前的某个时间运行,此时解释器可以自由返回到事件循环(执行完当时可能已经运行的所有操作)。这可能接近时间X,也可能是时间X之后的重要时间。

如果您确实需要在特定时间精确运行的内容,那么您可能需要一个比node.js实时得多的抢占式系统(不是node.js)。


您可以通过启动另一个node.js进程(可以使用child_process模块​​)在node.js中创建“变通方法”,并在该其他进程中启动一个程序,除了为您的计时器提供服务外,无需执行其他任何操作并执行与该计时器关联的代码。然后,至少您的计时器不会被其他可能正在运行的Javascript任务抢占,并且可以在接近所需时间的情况下运行。请记住,即使这种解决方法仍然不是真正的实时系统,但可能有一些用途。

否则,您可能希望使用具有抢占式计时器(可能甚至具有线程优先级)的更实时的系统语言来编写。

答案 2 :(得分:1)

我建议制作一个 cron ,它每n秒运行一次。如果您的程序很复杂,可能需要花费更多时间,则可以使用async

npm install cron

var CronJob = require('cron').CronJob;
new CronJob('* * * * * *', function() {
   console.log('You will see this message every second');

   callYourFunc();

}, null, true, 'America/Los_Angeles');

有关更多信息,请阅读此link

答案 3 :(得分:1)

也许您可以生成一个工作线程并在它等待工作时将其阻塞,这是通过SomePerformance在注释中建议的方式。这可能不是最优雅的方法,但至少可以将阻塞逻辑放在一边,以免影响应用程序的其余部分。

如果您不熟悉集群模块,请查看文档中的示例:https://nodejs.org/docs/latest-v10.x/api/cluster.html