【问题标题】:Locking on an object?锁定对象?
【发布时间】:2011-02-28 18:04:22
【问题描述】:

我是 Node.js 的新手,我确信有一个简单的答案,我只是找不到它:(

我正在使用文件系统来保存“包”(状态扩展名为“mypackage.idle”的文件夹)用户可以对它们执行操作,这会导致状态变为“qa”或“正在部署”等等......如果服务器正在接受大量请求并且多个请求进入同一个包,我将如何检查状态然后执行一个操作,这将改变状态,保证另一个请求之前没有改变它/动作发生期间?

所以在 c# 中是这样的

lock (someLock) { checkStatus(); performAction(); }

谢谢:)

【问题讨论】:

    标签: node.js locking


    【解决方案1】:

    如果 checkStatus() 和 performAction() 是一个接一个调用的同步函数,那么正如前面提到的其他人所说:它们的执行将不间断地运行直到完成。 但是,我怀疑实际上这两个函数都是异步的,组合它们的实际情况是这样的:

    function checkStatus(callback){
      doSomeIOStuff(function(something){
        callback(something == ok);
      });
    }
    
    checkStatus(function(status){
      if(status == true){
        performAction();
      }
    });
    

    上面的代码受竞争条件的影响,因为当 doSomeIOStuff 正在执行而不是等待它时,可以提供新的请求。

    您可能需要查看https://www.npmjs.com/package/rwlock 库。

    【讨论】:

      【解决方案2】:

      这有点误导。有许多脚本语言被认为是单线程的,但是当共享来自同一源的数据时,这会产生问题。当您运行单个请求时,NodeJs 可能是单线程的,但是当您有多个请求尝试访问相同的数据时,它的行为就像您运行多线程语言一样会产生相同的问题。

      这里已经有一个答案:Locking on an object?

      WATCH sentinel_key
      GET value_of_interest
      if (value_of_interest = FULL)
          MULTI
          SET sentinel_key = foo
          EXEC
          if (EXEC returned 1, i.e. succeeded)
              do_something();
          else
              do_nothing();
      else
          UNWATCH
      

      【讨论】:

      • 提供的链接是一个递归练习。
      • 模棱两可的答案。如果“数据”指的是文件系统、数据库和外部进程,那么锁定仍然是一个问题。如果这是指进程内数据结构,则锁定不是问题,如此处其他答案所述。
      • @RichardMarr 当然是。如果你有一个函数 T 链接了 10 个改变共享数据结构的异步操作(都在 Node.js 进程中),那么如果你连续运行两次 T 会发生什么?有一个竞争条件。
      • -1 所有这些争论,甚至没有人提到@lac_dev 用 CLIPS 编写的示例回答了 node.js 问题:) WTF
      • @Basezen 这个论点可能是我在 SO 上最不光彩的时刻,而且显然是沟通中断。两个人以最不利的方式解释彼此的答案并试图纠正它们。我敢肯定,作为一名专业人士和教育工作者,你知道最好不要加入并再次激起这一切。
      【解决方案3】:

      您可以做的一件事是锁定外部对象,例如,Oracle 或 Redis 等数据库中的序列。

      http://redis.io/commands

      例如,我正在使用带有 node.js 的集群(我有 4 个内核),并且我有一个 node.js 函数,每次运行它时,我都会增加一个变量。我基本上需要锁定该变量,因此没有两个线程使用该变量的相同值。

      看看How to create a distributed lock with Redis?

      还有这个https://engineering.gosquared.com/distributed-locks-using-redis

      如果你知道自己在做什么,我认为你可以带着这个想法去跑步。

      【讨论】:

        【解决方案4】:

        如果您使用回调进行异步调用,这意味着多个客户端可能会发出相同或相关的请求,并以不同的顺序接收响应。这绝对是锁定有用的情况。您不会在传统意义上“锁定线程”,而只是确保异步调用,并且它们的回调以可预测的顺序进行。 async-lock 包看起来可以处理这种情况。

        https://www.npmjs.com/package/async-lock

        【讨论】:

          【解决方案5】:

          警告,如果您添加日志条目,node.js 会更改语义,因为日志记录受 IO 限制。

          如果你改变了

          qa_action_performed = false
          function handle_request() {
            if (check_status() == STATUS_QA && !qa_action_performed) {
              qa_action_performed = true
              perform_action()
            }
          }
          

          qa_action_performed = false
          function handle_request() {
            if (check_status() == STATUS_QA && !qa_action_performed) {
              console.log("my log stuff");
              qa_action_performed = true
              perform_action()
            }
          }
          

          多个线程可以执行 perform_action()。

          【讨论】:

          • 您能提供更多相关信息吗?我的示例代码似乎不是这种情况。 process.nextTick(function(){ console.log('NEXT TICK'); }); for ( var i = 0; i
          • 这是不正确的。 console.log 是一个阻塞函数。即使不是这样,最糟糕的情况是控制台输出会混乱。
          【解决方案6】:

          您不必担心与 Node.js 的同步,因为它是带有事件循环的单线程。这是 Node.js 使用的架构的优势之一。

          checkStatus()performAction() 之间不会执行任何操作。

          【讨论】:

            【解决方案7】:

            node.js 中没有锁——因为你不应该需要它们。只有一个线程(事件循环),除非您执行 I/O 之类的异步操作,否则您的代码永远不会中断。因此,您的代码永远不应该阻塞。你不能做任何并行代码执行。

            也就是说,您的代码可能如下所示:

            qa_action_performed = false
            function handle_request() {
              if (check_status() == STATUS_QA && !qa_action_performed) {
                qa_action_performed = true
                perform_action()
              }
            }
            

            在 check_status() 和 perform_action() 之间没有其他线程可以中断,因为没有 I/O。一旦您进入 if 子句并设置 qa_action_performed = true,则不会有其他代码进入 if 块,因此 perform_action() 永远不会执行两次,即使 perform_action() 执行 I/O 需要时间。

            【讨论】:

            • 不错!谢谢你。那么即使 http.createServer 每秒可以异步处理数百个请求,一次也只有一个请求可以进入上面的“handle_request”函数?
            • 正如上面的代码,是的。如果您在 check_status() 中有异步 I/O,例如,因为您需要访问数据库或文件系统,您将使用回调,例如 check_status(function(result) { if (result == STATUS_QA && ... }). 在这种情况下,多个请求可以进入 handle_request(),但是使用 !qa_action_performed 仍然可以防止 perform_action() 的多次执行。
            • 我发现 Node.js 中的这种“无并行代码执行”模因令人困惑。如果您有两个任务(T1 和 T2)并且每个任务都需要将 10 个异步回调链接在一起,那么 T2 的一些回调将在 T1 等待异步操作完成时执行。这不是“并行”执行的 T1 和 T2 吗?如果 T1 和 T2 有一些共享的、不可重入的资源,他们是否不需要锁定该资源以确保这两个任务不会破坏它?
            • -1 代表because you shouldn't need them。你绝对需要它们(正如@mehaase 所提到的),除非你的应用只不过是一个计算器。
            • -1 相同。在异步 IO 操作可以在执行时相互中断的环境中,您怎么可能不需要访问信号量呢? (见@mehaase 和@freakish)
            猜你喜欢
            • 1970-01-01
            • 2013-12-03
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多