Allow running setInterval more than once per millisecond in nodejs
我有一个节点脚本,该脚本应该利用单个节点进程可以获得的所有CPU资源。但是我发现setInterval太慢了。
可以肯定的是,我在文档中找到了这一点:
When delay is larger than 2147483647 or less than 1, the delay will be
set to 1.
来源:https://nodejs.org/api/timers.html#timers_setinterval_callback_delay_args
现在,我想知道是否有办法进一步减少该限制,或者是否可以使用其他功能。
我不能只使用普通循环,因为还有其他异步事物需要同时运行。
编辑:
再说一遍:我不能只使用普通循环,因为还有其他异步事物需要同时运行。
我不确定为什么这么难理解。
在正常循环运行时,您将阻止其他所有程序的执行。是否将循环放入另一个异步执行的函数中都没关系。
这是什么意思?
让我们看一些例子:
1 2 3 4 5 | setInterval(()=>{console.log('a')},1000) // asynchronous thing that needs to run in the background while (true) { // do whatever } |
该代码将做什么?它将阻止所有内容。
1 2 3 4 5 6 | setInterval(()=>{console.log('a')},1000) // asynchronous thing that needs to run in the background setTimeout(()=>{ while (true) { // do whatever } }, 1) |
在while循环开始时,这也将阻止间隔的执行。
我相信这个问题属于节点而不是浏览器。您可以使用以下某些选项(递归/循环)来减少延迟时间。
setImmediate
setImmediate - Schedules the"immediate" execution of the callback after I/O events' callbacks. Returns an Immediate for use withclearImmediate ().When multiple calls to setImmediate() are made, the callback functions are queued for execution in the order in which they are created. The entire callback queue is processed every event loop iteration. If an immediate timer is queued from inside an executing callback, that timer will not be triggered until the next event loop iteration.
来自
setImmediate andsetTimeout are similar, but behave in different
ways depending on when they are called.
setImmediate() is designed to execute a script once the current poll phase completes.setTimeout( ) schedules a script to be run after a minimum threshold in ms has elapsed.
process.nextTick
The
process.nextTick() method adds the callback to the"next tick
queue". Once the current turn of the event loop turn runs to
completion, all callbacks currently in the next tick queue will be
called.
从
We recommend developers use setImmediate() in all cases because it's
easier to reason about (and it leads to code that's compatible with a
wider variety of environments, like browser JS.)
感谢Josh Lin提出的只运行多个时间间隔的想法。最后,我为
1 2 3 4 5 6 7 8 9 10 11 12 13 | function setInterval2(cb,delay) { if (delay >= 1) return [setInterval(cb,delay)]; var intervalArr = []; var intervalCount = Math.round(1/delay); for (var i=0; i<intervalCount; i++) intervalArr.push(setInterval(cb,1)); return intervalArr } function clearInterval2(intervalArr) { intervalArr.forEach(clearInterval); } |
它的工作原理与原始功能类似:
1 2 3 4 5 6 7 8 9 | var count = 0; // run interval every 0.01 milliseconds: var foo = setInterval2(function(){ count++; },0.01); // stop execution: clearInterval2(foo) |
1
1 2 3 4 5 6 7 8 9 10 11 | let count = 0, by = 100, _intervals = [], timelimit = 100 for (let i = 0; i < by; i++) { _intervals[i] = setInterval(() => count++, 1) } setTimeout(() => { _intervals.forEach(x => clearInterval(x)) console.log(`count:${count}`) }, timelimit) |
2.
1 2 3 4 5 6 7 8 9 10 11 12 | let count = 0, go = true recurser() setTimeout(() => { go = false console.log(`count:${count}`) }, 100) function recurser() { count++ go && setTimeout(recurser) } |
3.requestAnimationFrame运行少!
1 2 3 4 5 6 7 8 9 10 11 12 13 | let count = 0, go = true, timelimit = 100 step() setTimeout(() => { go = false, console.log(`count:${count}`) }, timelimit) function step() { count++ go && requestAnimationFrame(step) } |
因此,据我所知,多次运行
您问是否有可能
run setInterval more than once per millisecond in nodejs
正如您在问题中所指出的,使用
但是,您可以通过其他方式实现重复运行CPU密集型代码而不会造成不必要的延迟的目标。
如《 The Reason》的答案所述,
当浏览器为
下面的演示代码片段从此答案中借用了代码,以演示如何使用预先设置的计时器使延迟小于强制执行的延迟。在我测试的浏览器中,这通常会导致0毫秒的延迟。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | // First: repeat runCPUForAtLeast50ms() 10 times // with standard repeated setTimeouts and enforced delays testTimeout(10, function(){ // Then: repeat runCPUForAtLeast50ms() 10 times // using a repeated set of queued setTimeouts // circumventing the enforced delays testTimeout(10, false, true); }); function testTimeout(repetitions, next, multiple) { var delays = []; var lastCheck; var extraTimers; function runner() { if(lastCheck){ delays.push((+new Date) - lastCheck); } if(repetitions > 0) { //process chunk runCPUForAtLeast50ms(); //set new timer setTimeout(runner); } else if(repetitions === 0) { //report result to console console.log((multiple? 'Repeated multiple timers delays: ' : 'Repeated single timer delays: ') + delays.join(', ')); //invoke next() function if provided next && next(); } repetitions--; lastCheck = +new Date; } setTimeout(runner); if(multiple){ // make sure that there are always a fixed // number of timers queued by setting extra timers // at start extraTimers = 10; while(extraTimers--) setTimeout(runner); } } function runCPUForAtLeast50ms() { var d = (+new Date) + 50; while(+new Date < d); } |
简而言之,你不能。由于Javascript / Node是单线程应用程序,因此存在一些限制。这就是为什么您有异步中断。
长答案:
从计算机体系结构的角度来看,现代CPU和内核调度不是确定性的。如果您想要这种精细的控制,我建议您看一下没有内核调度程序的MCU和嵌入式解决方案。因为,您的OS还有许多其他进程以及占用CPU时间的内核进程,所以内核调度程序必须不断调度要在CPU上运行的不同进程并满足许多不同的需求。
即使设置为1毫秒,当您尝试测量时,它也可能不是1毫秒(确切的时间取决于操作系统,硬件和计算机上运行的进程数量)。
现在,如果要使用所有CPU资源,则不可能。
但是,如果您想尽可能多地利用资源,则可以探索当前的编程模式。例如,您可以调度一百万个线程(您的计算机可能无法处理),或疯狂的大量进程,并让调度程序将进程不断地放入CPU,因此没有空闲时间,并且最高CPU使用率。
另外,您可以运行CPU压力测试,这些测试旨在简单地使CPU最大化并保持其高温燃烧-确保已安装冷却解决方案。
我认为您可以使用异步模块解决您的问题...一种方法可能是:
1 2 3 4 5 6 7 8 9 10 | async.parallel([ (callback) => { // do normal stuff }, (callback) => { // do your loop } ], (err, results) => { // ... }); |
但是请考虑官方文档中的此说明...
Note: parallel is about kicking-off I/O tasks in parallel, not about
parallel execution of code. If your tasks do not use any timers or
perform any I/O, they will actually be executed in series. Any
synchronous setup sections for each task will happen one after the
other. JavaScript remains single-threaded.