Node.js中的递增计数器

zrfyljdw  于 2023-06-29  发布在  Node.js
关注(0)|答案(2)|浏览(233)

我想知道如何确保在Node.js环境中正确地递增计数器。
如果我理解正确的话,异步函数由非主线程处理,这些非主线程由4个libuv库线程示例化,回调由主线程执行(事件循环)。
如果上述为真,则以下代码可能会导致计数器的错误更新,因为f1()f2()可以尝试同时更新counter,最终结果为1而不是2
如何重写下面的代码以确保计数器的正确更新?

var counter = 0

async function f1() {
  // do something
  
  counter++
}

async function f2() {
  // do something
  
  counter++
}
f1()
f2()
camsedfj

camsedfj1#

不,f1f2不可能同时访问同一个counter变量,因此“最终”结果将是2。
然而,f1f2中的哪一个将其设置为12是不确定的。
异步函数仍然由唯一的JS处理运行,而不是后台线程。也许他们自己会等待一些由其他进程处理的任务,这取决于他们将进行的API调用,但JS部分仍然由唯一可以访问counter变量的JS处理运行。
所以这里没有冲突的风险。
唯一可能出现这种问题的情况是SharedArrayBuffer对象,当在不同的JS上下文中共享时(例如web workers)。在这种情况下,我们有Atomics
但是这里的counter变量不是SharedArrayBuffer,并且不能跨上下文共享。因此,当f1f2都以未确定的顺序解析时,counter将保持值2

jckbn6z7

jckbn6z72#

如果我理解正确的话,异步函数是由非主线程处理的

这不是真的所有的async函数都在主线程中处理。因此,调用f1()f2()始终导致计数器递增两次。它永远不会失败。这是因为JavaScript是单线程的。

重要的是要知道JavaScript是单线程的。即使是Node.js在多个线程中执行的操作,例如调用Web Worker或执行文件I/O,也会通过事件循环序列化回主线程。
除非你使用非标准的第三方线程库,否则node.js中的变量访问永远不会同时进行。这是因为变量不能在node.js中的worker之间共享(也就是说,如果你使用worker_threads -在正常的编码中,你甚至不会在单独的线程中执行自己的代码)。数据通过消息在线程之间交换-类似于互联网上的客户端和服务器彼此共享数据的方式:它们从不访问彼此的变量。

相关问题