Node.js进程跨平台同步:如何避免竞态条件与实现原子操作?
防止Node.js多实例启动与进程竞态处理
1. 单实例运行控制
最直接的方式是利用文件独占锁,通过操作系统的原子性文件操作检测是否已有实例在运行:
- 原生Node.js实现:
const fs = require('fs'); const lockPath = './.app-lock'; try { // 以独占创建模式打开文件,若文件已存在则抛出错误 fs.openSync(lockPath, 'wx'); // 注册退出时删除锁文件 process.on('exit', () => fs.unlinkSync(lockPath)); process.on('SIGINT', () => process.exit()); process.on('SIGTERM', () => process.exit()); } catch (err) { if (err.code === 'EEXIST') { console.error('已有脚本实例在运行,退出当前进程'); process.exit(1); } throw err; } // 后续业务逻辑 - 核心逻辑:
wx模式要求文件不存在才能创建,这是操作系统级的原子操作,不会出现竞态。
2. 通用进程竞态处理
对于共享资源(如配置文件、数据库操作)的竞态,除应用层锁外,可结合系统级原子操作:
- 文件类共享资源原子更新:先写入临时文件,再通过原子移动覆盖目标文件(跨平台通用):
const fs = require('fs').promises; const path = require('path'); async function atomicUpdateFile(targetPath, content) { const tempPath = `${targetPath}.tmp`; await fs.writeFile(tempPath, content); // 移动操作是原子的,要么完成要么失败 await fs.rename(tempPath, targetPath); }
Windows与Termux下的进程同步及原子操作工具
Windows平台
1. 原子目录创建
创建目录是操作系统级原子操作,可用来做进程锁:
mkdir "C:\temp\my-app-lock" || exit 1
如果目录已存在,mkdir会返回非零退出码,直接退出当前进程。
2. 文件原子更新
利用move命令的原子性更新文件:
echo "new content" > temp-config.txt move /Y temp-config.txt config.txt
/Y参数强制覆盖,NTFS下的移动操作是原子的,不会出现部分写入的情况。
3. 系统互斥量(进阶)
通过Windows内核互斥量实现进程同步,可借助Node.js的win32-api库直接调用系统API:
const { Win32API } = require('win32-api'); const kernel32 = new Win32API('kernel32.dll', ['CreateMutexW', 'ReleaseMutex']); // 创建命名互斥量,若已存在则返回已有句柄 const hMutex = kernel32.CreateMutexW(null, false, 'MyAppMutex'); if (hMutex === 0) { console.error('无法创建互斥量,或已有实例运行'); process.exit(1); } // 业务逻辑完成后释放互斥量 process.on('exit', () => kernel32.ReleaseMutex(hMutex));
Termux(Android Linux环境)
Termux完全兼容Linux的进程同步工具,常用方式:
1. flock文件锁
直接用flock命令包裹Node.js启动命令,实现单实例控制:
flock -n ./app.lock -c "node my-script.js"
-n参数表示非阻塞,若锁已被占用则直接退出,不会等待。
2. 原子目录创建
和Windows逻辑一致,Linux下创建目录也是原子操作:
mkdir ~/.my-app-lock || exit 1
3. 文件原子更新
Linux下mv命令同样是原子操作,实现方式:
echo "new content" > temp-config.txt mv temp-config.txt config.txt
内容的提问来源于stack exchange,提问作者irvnriir
相关产品推荐
相关产品推荐

