在JavaScript的非阻塞事件循环中,读取然后更改变量是否安全?如果两个进程想要几乎同时更改一个变量,会发生什么?
示例A:
这里是一个简化的示例,具有Express路线。假设该路由每秒被称为1000:
let counter = 0;
const getCounter = () => {
return counter;
};
const setCounter = (newValue) => {
counter = newValue;
};
app.get('/counter', (req, res) => {
const currentValue = getCounter();
const newValue = currentValue + 1;
setCounter(newValue);
});
示例B:
[如果我们执行更复杂的操作,例如Array.findIndex()
,然后执行Array.splice()
,该怎么办?可能是因为另一个事件进程已经更改了数组,所以找到的索引已经过时了吗?
const veryLargeArray = [
// ...
];
app.get('/remove', (req, res) => {
const id = req.query.id;
const i = veryLargeArray.findIndex(val => val.id === id);
veryLargeArray.splice(i, 1);
});
示例C:
如果在示例B中添加异步操作怎么办?
const veryLargeArray = [
// ...
];
app.get('/remove', (req, res) => {
const id = req.query.id;
const i = veryLargeArray.findIndex(val => val.id === id);
someAsyncFunction().then(() => {
veryLargeArray.splice(i, 1);
});
});
这个问题很难找到合适的词来描述。请随时更新标题。
根据@ThisIsNoZaku的链接,Javascript具有“运行完成”原则:
每条消息在处理任何其他消息之前都已完全处理。
这在推理程序时提供了一些不错的属性,包括这样的事实,即无论何时运行一个函数,都不能抢占它,并且会在其他任何代码运行之前完全运行(并且可以修改该函数操作的数据)。例如,这与C不同,在C中,如果函数在线程中运行,则运行时系统可能会在任何时候将其停止以在另一个线程中运行其他代码。
此模型的缺点是,如果一条消息花费太长时间才能完成,则Web应用程序将无法处理用户的交互,例如单击或滚动。浏览器通过“脚本运行时间过长”对话框缓解了这种情况。遵循的一种良好做法是缩短消息处理,并在可能的情况下将一条消息分解为几条消息。
进一步阅读:https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop
所以,用于:
示例A:这可以很好地用作站点计数器。
示例B:这也很好,但是如果同时发生许多请求,那么最后提交的请求将等待相当长的时间。
示例C:请参见示例B。
IMO,以延迟为代价,这解决了许多潜在的痛苦的并发问题。如果您必须优化请求的速度,那么我的建议是研究不同的体系结构(其他缓存等)。