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

发布于 2024-07-13 13:45:03 字数 902 浏览 7 评论 0原文

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

有任何想法吗?

Is there such a thing as an atomic test-and-set, semaphore, or lock in Javascript?

I have javascript invoking async background processes via a custom protocol (the background process literally runs in a separate process, unrelated to the browser). I believe I'm running into a race condition; the background process returns between my test and my set, screwing things up on the javascript side. I need a test-and-set operation to make it a real semaphore.

Here's the javascript code that attempts to detect background processes and queue them up:

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 is a singleton that implements the queuing; anybody that wants to invoke an external process does Call.call("something") .

Any ideas?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

荒岛晴空 2024-07-20 13:45:03

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

JavaScript has no locking semantics because JS is not a multi threaded language. Multiple threads can only operate concurrently in completely distinct contexts -- eg. HTML5 Worker threads, or in things like multiple instances of JavaScriptCore API's context object (I assume SpiderMonkey has a similar concept). They can't have shared state, so in essence all execution is atomic.

Okay, as you have now provided some of your code i assume you have something akin to:

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

Or some such (using appropriate APIs). In which case I would expect the JS engine to block if JS is executing in the context of your js object (which is what JavaScriptCore would do), failing that you will probably need to put a manual lock in place around js execution.

What engine are you using to do all of this? I ask because based on your description it sounds like you're setting a flag from a secondary thread from a non-JS language using the C/C++ API provided by that language, and most JS engines assume that any state manipulation made via the API will be occurring on a single thread, typically the same thread that all execution occurs on.

远昼 2024-07-20 13:45:03

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

First of all, while it is true that javaScript is single threaded, it is NOT true that no serialization mechanism is ever required by a javaScript application.

A simple example, is when a submit button should fade out for a set amount of time during which an Ajax request to a server is working. When the asynchronous Ajax request successfully completes then a message should appear where the button used to be.

While it would be nice to be able to cancel the button's fadeout and simply set its style to "display: none", as soon as the Ajax request completes, that is not possible in jQuery. Also, a solution could use Events to synchronize the two simultaneous activities, but that is essentially overkill for a simple problem.

A low-tech solution is to poll a lock and when the fadeout completes it is unlocked but the "server done" message is NOT displayed until the success callback, as set by $.post, executes.

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

Finally, I think this is more detailed answer, along the lines previously suggested in this discussion by perrohunter.

穿透光 2024-07-20 13:45:03

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

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

Maybe you could implement a basic integer semaphore, just add the variable into the DOM and lock/unlock it and make sure your functions keep checking it, else timeout them =)

If you are using a framework such as Mootools you could try to handle the flow of the app with events such as onComplete and so on.

装纯掩盖桑 2024-07-20 13:45:03

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

I'm not really sure what the question is asking exactly, but check out my semaphore object here: https://github.com/agamemnus/semaphore.js.

只涨不跌 2024-07-20 13:45:03

我有填充选择列表的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];
}

I have ajax stuff which populates select lists, I needed it to be locked so I did something like this. I think you could probably do it simpler though using deferreds and pipes or something.

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];
}
永不分离 2024-07-20 13:45:03

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

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 的并发/异步问题。

I had the same issue, here is how I solved it. It works fine for two concurrent processes. If you have three processes or more, it is possible that two processes start together.

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

It is better than the while loop because it solves concurrency/async issues for reading/setting isRunning.

路弥 2024-07-20 13:45:03

我使用 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;
      }
    });

I used the async-mutex package to solve an issue with Expo SQLite not blocking BEGIN TRANSACTION in iOS. You create the mutex and wrap your critical sections in the runExclusive method

    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;
      }
    });
夏见 2024-07-20 13:45:03

问题:一个函数以异步方式(并行)调用,但 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。

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

Problem: a function to be called in asynchronous way ( parallely ) but r function to be called in synchronous manner.
Similar to this

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 represents lock. 1 means lock is released, 0 means it is locked.
Now when first value reaches to the waitForCondition function, it would wait until the lock is released and will check it in every second for the lock to be released.The execution thread of first value ( if not lock ) would go to r function and lock the function. The execution thread of second value will wait until lock is released so the execution of second value will stick to waitForCondition statement and will not call r.

Keeping this as the base, you can then try to rebuild your logic.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文