Deno 2子进程信号处理问题:如何通过Ctrl-C仅终止文件监听子进程而不影响父进程?
Deno 2子进程信号处理问题:如何通过Ctrl-C仅终止文件监听子进程而不影响父进程?
这个问题我之前在Windows下用Deno的时候也踩过坑,先给你理清楚核心问题:Windows里的Ctrl-C信号是发给整个控制台进程组的,也就是说父进程和子进程会同时收到SIGINT。这就是为啥你在父进程加了信号监听器后,子进程反而收不到Ctrl-C的原因——父进程把信号拦截了。
下面给你两个实用的解决方案,按需选就行:
方案一:给子进程单独开控制台组(Windows专属)
这是最直接的Windows本地方案,原理是把子进程放到一个新的控制台组里,这样Ctrl-C的信号就只会发给子进程,父进程完全不受影响。
具体做法就是创建Deno.Command的时候,加上creationFlags: 0x00000010(这个是Windows API里的CREATE_NEW_PROCESS_GROUP标志),让子进程成为新控制台组的组长:
// 替换成你的监听脚本路径/命令 const watcherCmd = new Deno.Command("deno", { args: ["run", "--allow-read", "your-watcher-script.ts"], creationFlags: 0x00000010, // Windows专属:创建新进程组 }); const watcherProcess = watcherCmd.spawn(); const exitStatus = await watcherProcess.status; console.log(`子进程已退出,状态码:${exitStatus.code}`);
这样你按Ctrl-C的时候,只有子进程会收到SIGINT并终止,父进程会继续执行后面的代码。
方案二:父进程拦截信号后手动终止子进程(跨平台)
如果你的代码需要跨Windows、Linux、macOS运行,那上面的Windows专属标志就不适用了,这时候可以让父进程拦截SIGINT,然后手动给子进程发终止信号,父进程自己不退出。
示例代码如下:
let watcherProcess: Deno.ChildProcess | null = null; // 父进程拦截SIGINT信号 Deno.addSignalListener("SIGINT", () => { if (watcherProcess) { console.log("正在终止文件监听子进程..."); watcherProcess.kill("SIGINT"); watcherProcess = null; } // 注意:这里绝对不要调用Deno.exit(),否则父进程也会退出 }); // 启动子进程 watcherProcess = new Deno.Command("deno", { args: ["run", "--allow-read", "your-watcher-script.ts"], }).spawn(); // 等待子进程结束 const exitStatus = await watcherProcess.status; console.log(`子进程已退出,状态码:${exitStatus.code}`); // 记得移除信号监听器(可选,避免内存泄漏) Deno.removeSignalListener("SIGINT", () => {});
这种方式的好处是全平台通用,但要注意管理好子进程的引用,确保在信号触发时能正确拿到子进程实例去终止它。
额外注意点
- 如果你之前用的是
Deno.Command.output()方法,建议换成spawn(),因为output()会一直阻塞直到子进程结束,而spawn()能让你拿到子进程的引用,方便后续的终止操作。 - 方案一里的
creationFlags只在Windows下有效,在Linux/macOS下会被忽略,所以如果要写跨平台代码,最好加个判断:const isWindows = Deno.build.os === "windows"; const creationFlags = isWindows ? 0x00000010 : undefined; const watcherCmd = new Deno.Command("deno", { args: ["run", "--allow-read", "your-watcher-script.ts"], ...(isWindows && { creationFlags }), });
备注:内容来源于stack exchange,提问作者John Deighan
相关产品推荐
相关产品推荐

