【问题标题】:Avoiding race condition on Nodejs Api避免 Nodejs Api 上的竞争条件
【发布时间】:2020-12-16 09:10:48
【问题描述】:

我正在使用 Nodejs Api 服务器并面临一种特殊情况,即一群用户通过布尔指示通知我,并且只有当所有用户向我发送指示时,我才会调用一个方法来完成某些工作。

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

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
}

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

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

更新 - 上面的代码在路由异步方法中执行 -

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
    }
});

【问题讨论】:

    标签: node.js atomic race-condition


    【解决方案1】:

    如果你只使用单个 NodeJS 进程,它是单线程的,所以竞态条件不会发生在单个帧中

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

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

    以下是一些参考资料:

    但是,当

    • 运行多个 NodeJS 进程(在不同的机器上,或使用 NodeJS cluster 模块)。 => 在这种情况下,您不能将状态存储在 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>);
    }
    

    【讨论】:

    • 感谢您回答@Eloims!我更新了我的问题以澄清代码在异步路由中运行。我不确定我是否完全理解,您是说条件和下一行 - Group.actionPerformed = true 保证在单个帧中运行,尽管它们是在异步路由方法中编写的?
    猜你喜欢
    • 2019-01-10
    • 1970-01-01
    • 2015-01-30
    • 2010-09-25
    • 2010-09-25
    • 2019-06-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多