61

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") 。

有任何想法吗?

4

7 回答 7

26

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 进行的任何状态操作将发生在单个线程上,通常是所有执行都发生在同一个线程上。

于 2009-02-17T01:19:40.340 回答
2

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

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

于 2009-02-17T01:16:15.840 回答
2

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

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

虽然能够取消按钮的淡出并简单地将其样式设置为“显示:无”会很好,但只要 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 先前在本次讨论中建议的思路一致。

于 2015-10-04T00:24:03.167 回答
2

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

于 2017-01-15T02:31:43.413 回答
0

我有填充选择列表的 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];
}
于 2014-06-08T22:33:32.120 回答
0

我使用该async-mutex包解决了Expo SQLite 不阻止 iOS 中的 BEGIN TRANSACTION 的问题。您创建互斥体并将关键部分包装在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;
      }
    });
于 2021-07-08T15:53:31.360 回答
0

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

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

于 2019-10-30T05:48:44.707 回答