JavaScript中的竞争条件与复合赋值
我不是在谈论涉及networking或事件的复杂的竞争条件。 相反,我似乎已经发现+=
运算符在V8(Chrome 58或者Node 8)中不是primefaces的。
下面的代码旨在并行运行两个所谓的线程。 每个“线程”重复调用一个函数,该函数在睡眠几秒后返回其数字参数。 结果总结成一个累加器。
function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } // Return the passed number after sleeping that many seconds async function n(c) { await sleep(c * 1000); console.log('End', c); return c; } let acc = 0; // global // Call n repeatedly and sum up results async function nForever(c) { while (1) { console.log('Calling', c); acc += await n(c); // += not atomic?! console.log('Acc', acc); } } (async function() { // parallel repeated calls nForever(1); nForever(5.3); // .3 for sanity, to avoid overlap with 1 * 5 })();
问题是,约5秒后,我预计累加器为10.3(5次1 + 1次5.3)。 但是,这是5.3!
这不是一个竞争条件,因为你显式地使用await
产生执行。
该标准定义了诸如+=
的复合赋值不是primefaces的:复合赋值的左边在右边之前求值。 [ 1 ]
因此,如果您的RHS以某种方式更改acc
,则这些更改将被覆盖。 最简单的例子:
var n = 1; n += (function () { n = 2; return 0; })(); console.log(n);
事实上,在将acc += await n(c)
行replace为:
const ret = await n(c); acc += ret;
避免了比赛状况。
我的猜测是V8并没有优化acc += await n(c)
到n()
结果的ADD在包含acc
的内存位置上,而是将其扩展到acc = acc + await n(c);
,当nForever(5.3)
第一次调用时, acc
的初始值是0。
这对我来说是非直觉的,虽然不知道V8开发者会认为它是一个错误。