2579210
我对node的异步编程有一点点了解,不知道问的问题是否有价值,如果没什么价值,多包涵。 我知道node是单线程event loop的,没有抢占式“线程”,也就不存在一些memory race condition。 但如果出现逻辑上的race condition该怎么做?
当然即使需要lock,也不是多线程、多进程里面的锁,好像不需要os提供的原子操作?
我能想到下边这个例子。 例子比较简陋,只是想说明如果一次request handle需要 read和write各一次io,而且不希望read和write两次操作之间有数据被修改。 我在stackoverflow上搜类似的问题,有人提问,但是回答者寥寥。有回答提到AsyncLock这个库,我看到github上这个库,star很少,我试了一下这个库应该能满足一些要求。
下边这段代码print出来结果都是1(也就是hit没有被正确地自增)。
// Fake_Database is a fake database (or some other I/O)
class Fake_Database {
constructor() {
this._hit = 0;
}
// 异步做获取hit变量,类比从数据库中读
async get_hit() {
return this._hit;
}
// async set hit
// 异步写hit,类比向数据库中写
async set_hit(hit) {
this._hit = hit;
}
}
const service_fake_database = new Fake_Database();
// fake http handler
// 期望:每次有人访问handler1,hit++
async function handler1(req, res) {
const hit_old = await service_fake_database.get_hit();
const hit_new = hit_old + 1;
await service_fake_database.set_hit(hit_new);
res.success = true;
res.hit = hit_new;
}
// client A 和 client B 并发地 call `handler1(req,res)`,就有可能出现交错
// [A get hit] -> [B get hit] -> [A set hit] -> [B set hit]
// 两次handler1 call,但hit只会+1一次。
// 在内存层面上没有竞争,但是逻辑上有竞争
// 我知道数据库应该会有 原子自增
// 我只是想举个例子,假如出现某种情况一定要将读和写(2次io)分离呢?
// 但又不希望有其他“线程”(就是指 一个执行顺序,不是传统意义上的线程)在读和写之间 修改数据
// 比如我写的代码里的先读后写
let count = 0;
async function send_req() {
const req = {}, res = {};
await handler1(req, res);
console.log(`req${count++} -- ${res.hit}`);
}
async function main() {
for(let i=0;i<1000;i++){
send_req();
}
}
main();