【问题标题】:JavaScript async vs C# asyncJavaScript 异步与 C# 异步
【发布时间】:2019-05-22 21:37:51
【问题描述】:

编辑:找到答案 - https://www.youtube.com/watch?v=8aGhZQkoFbQ

_

好的,所以我有一些 C# 背景,而 C#“异步环境”有点混合并发和“small parallelism”,即当涉及到繁重的异步环境时,您 可以 em> 存在竞争条件、死锁,您需要保护共享资源。

现在,我试图了解 JavaScript/ES6 异步环境是如何工作的。考虑以下代码:

// current context: background "page"
// independent from content page
let busy = false;

// This is an event handler that receives event from content page
// It can happen at any time
runtimeObj.onMessage.addListener((request) =>
{
    if(request.action === 'AddEntry')
    {
        AddEntry(request);
        return true;
    }
    return false;
} );


function AddEntry(data)
{
    if (!busy)
        group.push({url: data.url, time: Date.now(), session: data.session});
    else
        setTimeout(AddEntry(data),10000) // simulating Semaphore wait
}

// called from asynchronous function setInterval()
function SendPOST()
{
    if (groups.length < 1 || groups === undefined)
        return;

    busy = true; // JS has no semaphores so I "simulate it"
    let del = [];
    groups.forEach(item =>
    {
        if (Date.now() - item.time > 3600000)
        {
            del.push(item);
            let xhr = new XMLHttpRequest();
            let data = new FormData();
            data.append('action', 'leave');
            data.append('sessionID', item.session);
            xhr.withCredentials = true;
            http.onreadystatechange = function()
            {
                if(http.readyState == 4 && http.status !== 200) {
                    console.log(`Unable to part group ${item.url}! Reason: ${http.status}. Leave group manually.`)
            }
}
            xhr.open('POST', item.url, true);
            xhr.send(data);
        }
    });
    del.forEach(item => groups.slice(item,1));
    busy = false;
}


setInterval(SendPOST, 60000);

这并不是最好的例子,因为它没有一堆与函数配对的 async 关键字,但在我的理解中,sendPost()AddEntry() 都不是真正的纯顺序操作。尽管如此,有人告诉我,在AddEntry() 中,busy 始终是false,因为:

sendPost 排队等待一分钟后执行

一个事件被添加到事件循环中

处理事件并调用 AddEntry

因为busy = false,组被推送

一分钟过去了,SendPost 被添加到事件循环中

事件正在处理,SendPost 被调用

groups.length === 1 所以它继续

忙 = 真

每个组都会导致一个请求排队

一个事件来了

忙 = 假

事件处理完毕,调用AddEntry

忙碌是假的,就像永远一样

群组被推送

最终,之前的请求得到了解决,并且 t 他将 onreadystatechange 回调放在事件循环中

最终处理每个回调并执行日志记录语句

这是正确的吗?据我了解,这基本上意味着不会出现竞争条件或死锁,或者我需要保护共享资源。

如果我是两个为 C# 运行时编写类似代码,其中 sendPost()AddEntry() 是异步任务方法,可以从那里的不同事件源以非阻塞方式调用 可能当我访问共享资源时迭代上下文被线程调度器在上下文切换中暂时挂起。

【问题讨论】:

  • 这个:这是正确的吗?据我了解,这本质上意味着不会出现竞争条件或死锁,或者我需要保护共享资源。 是的 - 它在 JavaScript 中称为“Run to Completion”。逻辑错误可能会产生竞争条件,开发人员希望在完成之前设置某些内容,但这是由于代码问题 - 而不是并发问题。
  • @RandyCasburn 是的,这就是我帖子中的quoted text 所解释的。
  • 正如我所怀疑的,只是想确定一下。 @jonas 给你一个很好的答案。
  • 你应该把这个:groups.length &lt; 1 || groups === undefined 改成这个!groups || groups.length &lt; 1 前者可能会报错。

标签: javascript c# asynchronous concurrency


【解决方案1】:

这是正确的吗?

是的,它充分描述了正在发生的事情。

JS 以单线程方式运行,这意味着函数将一直运行到结束。

 setTimeout(function concurrently() {
   console.log("this will never run");
 });

  while(true) console.log("because this is blocking the only thread JS has");

不能有竞争条件...

可以(以合乎逻辑的方式)。但是,它们不能出现在同步代码中,因为 JavaScript 运行单线程¹。如果您向某事添加回调或await 承诺,那么其他代码可能会在同时运行(但不会同时运行!),这可能会导致竞争条件:

 let block = false;

 async function run() {
   if(block) return // only run this once
   // If we'd do block = true here, this would be totally safe
   await Promise.resolve(); // things might get out of track here
   block = true;
   console.log("whats going on?");
}

run(); run();

...也没有死锁...

是的,它们是完全不可能的(除非您使用 Atomics¹)。

我从不需要保护共享资源。

是的。因为没有共享资源(好吧,除了 SharedBuffers¹)。


¹:通过使用 WebWorkers(或 NodeJS 上的线程),您实际上可以控制多个 JS 线程。但是,每个线程都运行自己的 JS 代码,因此不共享变量。这些线程可以在彼此之间传递消息,它们还可以共享特定的内存结构(SharedArrayBuffer)。然后可以同时访问它,并应用并发访问的所有效果(但您很少会使用它,所以...)。

【讨论】:

  • "因为没有共享资源[...]" /// 怎么会?我的代码中的groups 数组据说 是一个共享集合。我之所以这么说是因为我认为AddEntry() 可以随时执行,即使在sendPOST() 迭代它的那一刻,因为AddEntry() 技术上的回调。此外,您提供的代码示例毫无意义,您故意错误地锁定,您不要那样使用锁定。我说的是在不使用锁时由于并发导致的竞争条件。
  • @Kirikan 您对“共享资源”的定义是什么(在线程安全的上下文中)?由于常规 JavaScript 是单线程的(正如 Jonas Wilms 在答案中提到的那样),如果常规线程安全意义(由多个线程访问的资源),则没有“共享资源”......
  • @Kirikan 答案中没有代码锁定-您能否澄清您的意思是“您故意错误锁定”? (再次 - 锁对单线程环境没有意义)
  • @kirikan 没有。没有代码将同时运行。是的AddEntry 是一个回调。这意味着代码可能会在调用之前完成之后运行,但函数本身只有在没有其他代码执行时才会运行。
  • @Kirikan JavaScript 中没有“上下文切换”。所有代码同步运行直到完成,并且只有在该引擎将选择下一段代码同步运行之后。 “下一块”是顶级代码(下一个 JS 文件)或下一个回调(外部,如 click/http 响应或计时器)。 await/async 是在 Promise 解析上构造回调的好语法(它本身只是回调的包装)。
猜你喜欢
  • 2018-08-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-30
  • 1970-01-01
  • 1970-01-01
  • 2018-12-06
  • 1970-01-01
相关资源
最近更新 更多