【问题标题】:Javascript semaphore / test-and-set / lock?Javascript信号量/测试和设置/锁定?
【发布时间】:2010-10-07 23:58:02
【问题描述】:

Javascript 中是否存在诸如原子测试和设置、信号量或锁定之类的东西?

我有 javascript 通过自定义协议调用异步后台进程(后台进程实际上在单独的进程中运行,与浏览器无关)。我相信我正在陷入竞争状态;后台进程在我的测试和我的设置之间返回,在 javascript 方面搞砸了。我需要一个测试和设置操作来使它成为一个真正的信号量。

这是尝试检测后台进程并将它们排队的 javascript 代码:

Call = function () {

var isRunning = true,
    queue = [];

return  {
    // myPublicProperty: "something",

    call: function (method) {
            if (isRunning) {
                console.log("Busy, pushing " + method);
                queue.push(method);
            } else {
                isRunning = true;
                objccall(method);
            }
        },

        done: function() {
            isRunning = false;
            if (queue.length > 0) {
                Call.call(queue.shift());
            }
        }
    };
}();

Call是一个实现排队的单例;任何想要调用外部进程的人都会调用 Call.call("something") 。

有什么想法吗?

【问题讨论】:

标签: javascript concurrency semaphore


【解决方案1】:

JavaScript 没有锁定语义,因为 JS 不是多线程语言。多个线程只能在完全不同的上下文中同时运行——例如。 HTML5 Worker 线程,或者在 JavaScriptCore API 的上下文对象的多个实例中(我假设 SpiderMonkey 有类似的概念)。它们不能共享状态,所以本质上所有的执行都是原子的。

好的,既然你现在已经提供了一些代码,我假设你有类似的东西:

External Process:
<JSObject>.isRunning = true;
doSomething()
<JSObject>.done()

或类似的(使用适当的 API)。在这种情况下,如果 JS 在您的 js 对象的上下文中执行(这是 JavaScriptCore 会做的),我希望 JS 引擎会阻塞,否则您可能需要在 js 执行周围设置手动锁定。

您使用什么引擎来完成所有这些工作?我问是因为根据您的描述,听起来您正在使用该语言提供的 C/C++ API 从非 JS 语言的辅助线程设置标志,并且大多数 JS 引擎假定通过 API 进行的任何状态操作将发生在单个线程上,通常是所有执行都发生在同一个线程上。

【讨论】:

  • 我添加了上面的完整代码以使其更加清晰。引擎是 Webkit (safari)。我从javascript调用外部进程,外部进程在完成后调用“done”方法。基本上我需要确保一次只运行一个外部进程。
  • 假设您正在执行类似于 [someObject call] 或 JSObjectCallAsFunction(context, doneFunction, someObject) 的操作,您应该确保在主线程上调用,尤其是在使用WebKit 提供的 ObjC JS API(IIRC 它可能会绕过一些锁)
  • 你有没有在iPhone上使用过webkit sdk?我正在使用一个简单的 window.location 方案,还没有尝试过 SDK。希望了解有关 SDK 的更多信息。
  • 不,我对 iPhone SDK 知之甚少——我只知道 JSC 的 C api :D
【解决方案2】:

首先,虽然 JavaScript 确实是单线程的,但 JavaScript 应用程序并不需要任何序列化机制。

一个简单的例子是,提交按钮应该淡出一段设定的时间,在此期间,对服务器的 Ajax 请求正在工作。当异步 Ajax 请求成功完成后,应该会在按钮所在的位置显示一条消息。

虽然能够取消按钮的淡出并简单地将其样式设置为“display: none”会很不错,但只要 Ajax 请求完成,这在 jQuery 中是不可能的。此外,一个解决方案可以使用事件来同步两个同时进行的活动,但这对于一个简单的问题来说基本上是多余的。

一个低技术的解决方案是轮询一个锁,当淡出完成时它被解锁,但在 $.post 设置的成功回调执行之前不会显示“服务器完成”消息。

var gl_lock;
var gl_selfID;

function poll_lock(message) {
     if (gl_lock === 0) {
          $('#output').text(message).fadeIn(200);
          window.clearInterval(gl_selfID);
     }
 } // end of poll_lock

 function worker() {

     // no one gets in or out
     gl_lock = 1;

     $.post(..., data,function() { 
           gl_selfID = window.setInterval(poll_lock, 40, data.message);
      }, "json");

     // end of fadeout unlock the semaphore
     $('#submit-button').fadeOut(400, function() { gl_lock = 0; });

  } // end of worker

最后,我认为这是更详细的答案,与 perrohunter 先前在此讨论中建议的思路一致。

【讨论】:

    【解决方案3】:

    也许你可以实现一个基本的整数信号量,只需将变量添加到 DOM 并锁定/解锁它并确保你的函数不断检查它,否则它们会超时 =)

    如果您使用 Mootools 等框架,您可以尝试使用 onComplete 等事件来处理应用程序的流程。

    【讨论】:

      【解决方案4】:

      我不太确定问题到底在问什么,但请在此处查看我的信号量对象:https://github.com/agamemnus/semaphore.js

      【讨论】:

      • 是的。项目html页面中有几个使用示例。
      • 这看起来并不像信号量,它看起来更像是一个任务调度程序,它允许在排队任务之前进行一定程度的并行性。要成为信号量,您需要能够阻塞直到资源可用。
      • 它确实会阻塞,但我可能不清楚这意味着什么。
      • 它不会“阻塞”,它会将控制权交给 javascript 调度程序,然后稍后恢复。但从外部看,它看起来和行为就像一个信号量。在内部,它比阻塞信号量“更智能”。
      【解决方案5】:

      我有填充选择列表的 ajax 东西,我需要锁定它,所以我做了这样的事情。我认为你可以通过使用延迟和管道或其他东西来做更简单的事情。

      var semaphore=[];
      
      function myFunc(arg){
         var dfd;
         $.when(semaphore[table]).done(
            function(){
                  dfd=myFuncInner(arg);
            }
            );
      return dfd;
      }
      
      function myFuncInner(table){
      semaphore[arg] = new $.Deferred();
      ... 
      somethingasynchronous({
          semaphore[arg].resolve();
      });
      
      return  semaphore[arg];
      }
      

      【讨论】:

        【解决方案6】:

        我遇到了同样的问题,我是这样解决的。它适用于两个并发进程。如果您有三个或更多进程,则可能会同时启动两个进程。

        var isRunning = false;
        ...
        var id = setInterval(function(){ //loop until isRunning becomes false
                    if (!isRunning){
                        isRunning = true;
                        //do something synchronous or use a promise
                        isRunning = false;
                        clearInterval(id); // stop the loop here
                    }
                , 10);
        

        它比 while 循环更好,因为它解决了读取/设置 isRunning 的并发/异步问题。

        【讨论】:

          【解决方案7】:

          我使用async-mutex 包解决了Expo SQLite not blocking BEGIN TRANSACTION in iOS 的问题。您创建互斥体并将关键部分包装在 runExclusive 方法中

              return this.mutex.runExclusive(async () => {
                try {
                  await this.executeSqlAsync('begin immediate transaction');
                  const tx = new AsyncSQLTransaction(this);
                  const rs = await callback(tx);
                  await this.executeSqlAsync('commit');
                  return rs;
                } catch (error) {
                  await this.executeSqlAsync('rollback');
                  throw error;
                }
              });
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多