首页 > 解决方案 > 异步函数对同一对象的并发写访问会导致无效值吗?

问题描述

我想知道下面的代码是否会导致i对象context中的整数具有无效值。到目前为止,我无法在我的实验中引起问题。

// Node.js (v10.19.0)

const Promise = require('promise')

// object accessed from different async processes in parallel
let context = {i: 0}

// create async functions
let funkMaker = function(i) { 
  return async function(context) { 
    context.i += 1; 
    console.log(`Funk${i} at ${Date.now()}: Incremented argument 'context.i' to: ${context.i}`); 
    return context.i 
  } 
}

// create async processes
let funksArr = []; 
for (k=1; k<101; k++) { 
  funksArr.push(funkMaker(k)) 
}

// invoke async processes in parallel
Promise.all(
  funksArr.map(f => f(context))
).then(
  x => console.log(`Promise.all resolved with ${x}`)
).catch(
  e => console.log(`Promise.all raised an error:\n${e}`)
)

以上产生以下输出(摘录):

Funk1 at 1584448782621: Incremented argument 'context.i' to: 1   
Funk2 at 1584448782621: Incremented argument 'context.i' to: 2   
Funk3 at 1584448782622: Incremented argument 'context.i' to: 3   
Funk4 at 1584448782622: Incremented argument 'context.i' to: 4   
Funk5 at 1584448782622: Incremented argument 'context.i' to: 5   
Funk6 at 1584448782622: Incremented argument 'context.i' to: 6   
Funk7 at 1584448782622: Incremented argument 'context.i' to: 7    
Funk8 at 1584448782622: Incremented argument 'context.i' to: 8  
Funk9 at 1584448782622: Incremented argument 'context.i' to: 9

正如你所看到的,有几个进程似乎context.i在同一毫秒(Funk3Funk9上面)访问。我对为什么整数context.i仍然正确递增且没有错误感到困惑。

见解是最受欢迎的。谢谢!

更新

为了在真正的系统线程中尝试这一点,我调整了从这篇博客文章中窃取的代码,该代码使用crypto并隐式libuv地在真实线程中执行。我仍然无法context.i休息。老实说,我仍然感到困惑。

const crypto = require("crypto");
const start = Date.now();

let context = {i: 0}

function logHashTime(context) {
  crypto.pbkdf2("a", "b", 100000, 512, "sha512", () => {
    context.i += 1;
    console.log(`Hash: ${Date.now() - start}, 'context.i' incremented to: ${context.i}`);
  });
}

for (i = 0; i < 100; i++) {
  logHashTime(context)
}

输出(摘录)仍然是“好的”:

Hash: 1268, 'context.i' incremented to: 1  
Hash: 1460, 'context.i' incremented to: 2  
Hash: 1660, 'context.i' incremented to: 3  
Hash: 1907, 'context.i' incremented to: 4  
Hash: 2493, 'context.i' incremented to: 5  
Hash: 2673, 'context.i' incremented to: 6  
Hash: 3154, 'context.i' incremented to: 7  
Hash: 3215, 'context.i' incremented to: 8  
Hash: 3662, 'context.i' incremented to: 9

标签: javascriptnode.jsasynchronouses6-promise

解决方案


首先,这里没有实际的异步代码。当您的函数被声明async时,它的函数体仍然同步执行,并且在该函数体中没有实际的异步操作。它被标记为的事实async意味着它返回一个承诺,但这就是您的示例中的所有变化。

其次,Javascript 在单个线程中运行您的 Javascript,除非您明确使用工作线程(您没有在那里使用)。

因此,由于一切都是同步的,并且都在单个线程中运行,因此代码只是按照.map()循环运行的顺序可靠地运行。这里唯一真正异步的是当async函数返回的承诺调用它们的.then()处理程序时,它允许Promise.all()调用它的.then()处理程序。但是,所有结果都是在此之前同步生成的。

正如你所看到的,有几个进程似乎在同一毫秒访问 context.i(上面的 Funk3 到 Funk9)。我对为什么整数 context.i 仍然正确递增且没有错误感到困惑。

它们都按顺序一次运行一个。您看到相同的毫秒只是因为您没有足够的计时器分辨率来实际看到执行时间的差异。没有一个功能实际上是“同时”运行的。

context.i正确递增,因为这些函数实际上是按顺序一个接一个地调用和运行的。


推荐阅读