首页 > 解决方案 > node.js:如何锁定/同步代码块?

问题描述

让我们看一下简单的代码片段:

var express = require('express');
var app = express();
var counter = 0;

app.get('/', function (req, res) {
   // LOCK
   counter++;
   // UNLOCK
   res.send('hello world')
})

假设这app.get(...)被调用了很多次,并且您可以理解我不希望该行counter++由两个不同的线程同时执行。

因此,我想锁定这一行,只有一个线程可以访问这一行。我的问题是如何在 node.js 中做到这一点?

我知道有一个 lock package: https://www.npmjs.com/package/locks,但我想知道是否有一种“本机”方式可以在没有外部库的情况下执行此操作。

标签: node.js

解决方案


我不希望行 counter++ 由两个不同的线程同时执行

仅使用常规 Javascript 编码在 node.js 中不会发生这种情况。

node.js 是单线程和事件驱动的,因此一次只能运行一段 Javascript 代码来访问该变量。您不必担心多线程系统的典型抢先并发问题。

也就是说,如果您使用异步代码,node.js 中仍然可能存在并发问题,因为 node.js 异步模型将控制权返回给系统以处理下一个事件,并且异步回调在某个未来事件上被调用。但是,并发问题是非先发制人的,因此您可以完全控制它们何时发生。

如果您在app.get()路由处理程序中向我们展示您的实际代码,那么我们可以更具体地建议您是否存在并发问题。而且,如果您这样做,我们可以建议如何最好地处理它。

线程池中的线程都是在后台运行的原生代码。它们仅通过事件队列对事件进行排队来触发实际的 Javascript 运行。所以,因为所有运行的 Javascript 都是通过事件队列序列化的,所以一次只能运行一个 Javascript。事件队列的基本方案是解释器运行一段 Javascript,直到将控制权返回给系统。此时,解释器查看事件队列,如果有事件等待,它会拉出该事件并调用与该事件关联的回调。同时,如果后台有原生代码在运行,当它完成时,它会在事件队列中添加一个事件。在当前 Javascript 将控制权返回给系统之前,不会处理该事件,然后它可以从事件队列中获取下一个事件。所以,正是这个事件队列序列化了一次只运行一段 Javascript。

编辑: Nodejs 现在确实有 WorkerThreads 可以启用单独的 Javascript 线程,但是每个线程都有自己的堆和自己的变量,因此来自一个线程的变量不能直接从另一个线程访问。您可以配置两个 WorkerThread 都可以访问的共享内存,但这不是直接变量,而是内存块,如果您想使用共享内存,那么您确实需要编写自己的同步方法以确保您是原子访问变量。您在问题中显示的代码没有使用任何这些,因此对counter变量的访问已经是原子的,并且即使您使用的是 WorkerThreads,也不能同时被任何其他 Javascript 访问。


推荐阅读