我正在编写一个node.js服务器脚本,该脚本异步地为多个客户端使用共享的文本列表数据。
客户端可以读取、添加或更新此共享列表的项。
static getitems(){
if (list== undefined) list = JSON.parse(fs.readFileSync("./list.json"));
return list;
}
static additem(newitem){
var key = Object.keys(newitem)[0];
list[key] = newitem[key];
fs.writeFileSync("./list.json", JSON.stringify(list));
}
客户端可以使用以下快速api修改和获取列表数据
app.get("/getlist"), (req, res)=>{
res.send(TempMan.getTemplates());
});
app.post("/addlist"), (req, res)=>{
additem(req.body.newitem)
res.status(204).end()
});
在c#, c++和其他桌面编程语言的长期背景下,虽然我认为javascript没有遇到竞争条件,但我很担心资源共享将成为一个问题。我首先想到的是信号量或共享锁或其他语言中的一些其他多线程管理解决方案,但阅读javascript不需要这样的方法。
这样的node.js实现运行到资源共享问题,如文件读/写的同时尝试?我怎么解决这个问题?我需要某种交易功能,我可以在javascript中使用?
一般来说,Node.js程序可以遇到一个资源共享问题,我们通常称之为竞争条件";问题。这不是由于两个线程/进程,而是由于内在属性:async。假设有两个异步函数,第一个已经启动但还没有完成,它里面有一些await
,在这种情况下,第二个异步函数可以启动。如果它们在各自的代码块中访问相同的资源,可能会导致竞争条件。
我做了一张幻灯片来介绍这个问题:https://slides.com/grimmer/intro_js_ts_task_queuelib_d4c/fullscreen#/0/12.
返回到你的示例代码,你的代码WILL NOT有任何竞态条件。即使你在express路由回调中使用任何async函数而不是fs.writeFileSync
,原因是express的实现将等待第一个异步路由回调处理函数,并且只有在第一个异步路由回调处理函数完成后才开始执行第二个异步路由回调处理函数。 例如:
app.post('/testing1', async (req, res) => {
// Do something here
});
app.post('/testing2', async (req, res) => {
// Do something here
});
就像下面的代码在实现Express,
async function expressCore() {
await 1st_routing_call_back()
await 2nd_routing_call_back()
}
但是请记住,其他服务器框架可能没有相同的行为。https://www.apollographql.com/和https://nestjs.com/都允许同时执行两个异步路由方法。像下面的
async function otherServerFrameworkCore() {
1st_routing_call_back()
2nd_routing_call_back()
}
,你需要找到一种方法来避免竞争条件,如果这是你关心的。要么使用事务数据库使用,要么使用一些轻量级的npm同步库,适合单个Node.js实例程序,例如https://www.npmjs.com/package/d4c-queue,这是我做的。多Node.js实例是多进程的,应该有可能存在竞争条件问题,而DB事务是一个更合适的解决方案。