Nodejs 全局变量的读写并发
Nodejs 全局变量的读写并发
var a = {};为全局变量,write(a)基于事件,可对a进行修改。 想问的是,当一个子线程收到事件,正在对a进行修改的同时,主线程调用get(a)尝试读取a的值。会怎样?
Node.js 全局变量的读写并发
在Node.js中,全局变量可以在不同的执行上下文中共享。然而,由于JavaScript本身是单线程的,因此通常不会遇到典型的多线程并发问题。但是,如果你使用某些库或者自定义逻辑来模拟多线程行为(例如通过worker_threads
模块),那么就需要考虑并发访问的问题。
示例场景
假设我们有一个全局变量 a
,并且我们希望在一个子线程中修改它,并且在主线程中读取它的值。以下是一个简单的示例:
// 主线程代码
const { Worker, isMainThread, parentPort } = require('worker_threads');
if (isMainThread) {
global.a = {};
const worker = new Worker(__filename);
// 子线程修改全局变量
worker.on('message', (msg) => {
console.log(`主线程接收到消息: ${JSON.stringify(msg)}`);
});
// 主线程读取全局变量
setTimeout(() => {
console.log(`主线程读取到的a:`, global.a);
}, 1000);
} else {
// 子线程代码
global.a.b = 'hello';
parentPort.postMessage(global.a);
}
在这个例子中,主线程创建了一个新的工作线程(Worker
),并在主线程中设置了一个全局变量 a
。子线程修改了这个全局变量,然后将结果发送回主线程。主线程在一段时间后读取这个全局变量。
并发问题
如果我们在子线程修改 a
的同时,主线程立即读取 a
的值,可能会遇到并发问题。例如:
// 主线程代码
const { Worker, isMainThread, parentPort } = require('worker_threads');
if (isMainThread) {
global.a = {};
const worker = new Worker(__filename);
// 子线程修改全局变量
worker.on('message', (msg) => {
console.log(`主线程接收到消息: ${JSON.stringify(msg)}`);
});
// 主线程立即读取全局变量
console.log(`主线程立即读取到的a:`, global.a);
} else {
// 子线程代码
global.a.b = 'hello';
parentPort.postMessage(global.a);
}
在这种情况下,主线程可能在子线程完成修改之前读取到不完整的或未初始化的值。为了避免这种情况,可以使用锁机制或者确保在读取之前等待写操作完成。
锁机制示例
为了确保并发安全,我们可以使用一个简单的锁机制来同步读写操作:
const { Worker, isMainThread, parentPort } = require('worker_threads');
if (isMainThread) {
global.a = {};
global.lock = false;
const worker = new Worker(__filename);
worker.on('message', (msg) => {
console.log(`主线程接收到消息: ${JSON.stringify(msg)}`);
});
// 使用锁机制确保读写操作的顺序
worker.on('online', () => {
global.lock = true;
setTimeout(() => {
global.lock = false;
console.log(`主线程读取到的a:`, global.a);
}, 1000);
});
} else {
global.a.b = 'hello';
global.lock = false;
parentPort.postMessage(global.a);
}
在这个示例中,我们使用了一个全局变量 lock
来控制读写操作的顺序。主线程在读取全局变量之前等待 lock
变为 false
,从而确保读取时全局变量已经被正确修改。
通过这种方式,我们可以避免并发读写导致的数据不一致问题。
Node中执行js程序时是单线程的,不可能出现这个问题
我一直困惑,在event loop里面,某个事件被激活要去执行的时候,是node主进程去执行,还是底层C代码会fork一个子线程去执行? 如果是后者,怎么保证不冲突呢。
如果如你所说不可能出现,我想设计一个用例去证明。这个用例的设计能否不吝赐教?
是的,nodejs是单线程异步回调,里面只有进程的概念了
在Node.js中,全局变量(如var a = {}
)的读写并发问题可以通过多种方式解决。Node.js本身是单线程的,但在处理异步操作时可能会遇到并发问题。为了确保全局变量的安全访问和修改,可以使用锁机制或Promises来同步对全局变量的操作。
示例代码
假设我们有一个全局对象 a
,并且有两个函数 write(a)
和 get(a)
分别用于写入和读取该对象。我们可以使用 async/await
来确保这些操作是顺序执行的。
let a = {};
const lock = false;
// 模拟写入操作
function write(newValue) {
return new Promise((resolve, reject) => {
if (lock) {
setTimeout(() => write(newValue), 100); // 如果被锁定,则等待100ms后重试
return;
}
lock = true; // 加锁
try {
a = newValue;
console.log('Write:', a);
lock = false; // 解锁
resolve();
} catch (error) {
lock = false; // 解锁
reject(error);
}
});
}
// 模拟读取操作
function get() {
return new Promise(resolve => {
if (lock) {
setTimeout(() => get(), 100); // 如果被锁定,则等待100ms后重试
return;
}
lock = true; // 加锁
try {
console.log('Read:', a);
lock = false; // 解锁
resolve(a);
} catch (error) {
lock = false; // 解锁
reject(error);
}
});
}
// 使用示例
(async () => {
await write({ key: 'value' });
const result = await get();
console.log('Final Value:', result);
})();
解释
- 加锁与解锁:我们在写入和读取操作之间添加了锁机制,以确保在同一时间只有一个操作在执行。
- 异步操作:通过返回
Promise
,我们可以使用async/await
来确保操作按顺序执行。 - 重试机制:如果当前操作被锁定,则将操作延迟100毫秒后重试,这有助于避免竞态条件。
这种方法虽然简单,但适用于简单的场景。对于更复杂的应用,建议使用专门的锁库或消息队列来管理并发操作。