避免Nodejs Api上的竞争条件



我使用的是Nodejs Api服务器,面临着一种特殊的情况,一群用户用布尔指示通知我,只有当所有用户都向我发送了指示时,我才会调用一个方法来完成一些工作。

因此,对于这个例子,我创建了一个由5个连接用户组成的组,并等待他们的指示,其使用具有输入布尔值的HttpPost消息来发送。因此,在服务器上,我持有一个对象,如下所示-

Group = {
actionPerformed: false,
userOne: false,
userTwo: false,
userThree: false,
userFour: false,
userFive: false
}

在收到来自以下任何用户的消息后,我会更新相关属性,比如说,对于userOne,我将Group.userOne属性设置为true。然后我检查所有其他用户是否已经发送了他们的指示,所以我执行以下测试-

if (!Group.actionPerformed && 
Group.userOne && 
Group.userTwo && 
Group.userOne && 
Group.userThree && 
Group.userFour && 
Group.userFive) {
Group.actionPerformed = true;
//do something only once
}

当然,我只想执行括号中的上述代码一次,因此我想避免出现竞争条件的情况,即最后两个用户在完全相同的时间发送他们的指示,并且两个用户都将其属性设置为true,然后在检查条件时,第一个用户可能会检查条件-结果为true,在将actionPerformed设置为true之前,可能会发生线程切换,第二个用户将测试条件,结果也为true,然后两个用户都将进入括号。

所以我的问题是,所描述的情况只能通过对条件和Group.actionPerformed=true的原子操作来解决吗?或者,还有其他解决方案吗,也许更优雅?

UPDATE-上面的代码是在一个路由异步方法-中执行的

router.route('/')
.get(passport.authenticate('jwt', { session: false }), async (req, res, next) => {
....
if (!Group.actionPerformed && 
Group.userOne && 
Group.userTwo && 
Group.userOne && 
Group.userThree && 
Group.userFour && 
Group.userFive) {
Group.actionPerformed = true;
//do something only once
}
});

如果您只使用单个NodeJS进程,它是单线程的,因此竞争条件不能在单个帧中发生

另一种说法是:当代码执行以响应事件时,它不会被中断。

您可以看到,如果在服务器中输入无限循环,进程将不会响应任何其他查询(JS中没有线程(。

以下是一些参考资料:

  • https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop
  • https://blog.carbonfive.com/the-javascript-event-loop-explained/

但是,当

  • 运行多个NodeJS进程(在不同的机器中,或使用NodeJScluster模块(。=>在这种情况下,您无法将状态存储在NodeJS进程内存中

  • 在设置布尔值和检查它们是否都已设置之间执行任何异步工作(读取文件、异步/等待、网络…(。=>改变这种行为

// This will always work, as js frames run to completion
async function toggle(userName) {
Group[userName] = true;
[...all the SYNCHRONOUS work you want...]
if (!Group.actionPerformed && Group.userOne && ... && Group.userFive) {
Group.actionPerformed = true;
//do something only once
}
}
// This may not work. A race condition is possible
async function toggle(userName) {
Group[userName] = true;
await database.get(somedocument); // this is asynchronous
// the code below this line will not run in the same frame
// so other javascript code may run in between
if (!Group.actionPerformed && Group.userOne && ... && Group.userFive) {
Group.actionPerformed = true;
//do something only once
}
}
// This may not work. A race condition is possible
async function toggle(userName) {
Group[userName] = true;
setTimeout(() => {
if (!Group.actionPerformed && Group.userOne && ... && Group.userFive) {
Group.actionPerformed = true;
//do something only once
}
}, <any value>);
}

相关内容

  • 没有找到相关文章

最新更新