Javascript 信号量/测试和设置/锁定?

问题描述 投票:0回答:8

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


4
投票

首先,虽然 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 之前在本讨论中建议的思路。


2
投票

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

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


2
投票

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


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

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


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

0
投票

问题:a 函数以异步方式(并行)调用,但r 函数以同步方式调用。 类似这个

a->b->
a->.  b->
a->.     b->


const arr = [1, 2, 3, 4, 5];
let S = 1;
const a = (val) => {
  return new Promise((res, _rej) => setTimeout(() => {
    console.log("value is processed by a ", val);
    res(val);
  }, 4000));
};
const r = (val) => {
  setTimeout(() => {
    console.log("value is processed by b ", val);
    S = 1;
  }, 100);
};
const condition1 = () => S==1
async function waitForCondition(condition) {
    while (!condition()) {
        await new Promise(resolve => setTimeout(resolve, 1000)); // wait for 1 second
    }
    S=0;
}
arr.forEach((v) => {
    a(v).then(async () => {
        await waitForCondition(condition1);
        r(v);
    });
})

S 代表锁定。 1表示锁已释放,0表示已锁定。 现在,当第一个值到达 waitForCondition 函数时,它将等待直到锁被释放,并每秒检查一次是否释放锁。第一个值的执行线程(如果不是 lock )将转到 r 函数并锁定功能。第二个值的执行线程将等待直到锁被释放,因此第二个值的执行将坚持waitForCondition语句并且不会调用r。

以此为基础,你可以尝试重建你的逻辑。

© www.soinside.com 2019 - 2024. All rights reserved.