如何在Swift Package Manager构建的macOS CLI中捕获SIGINT与SIGTERM信号
在Swift PM的macOS CLI中捕获退出信号以清理资源
我正在使用Swift Package Manager开发一款面向macOS的CLI(可执行产品)。我的worker进程会处理数据并将输出写入文件,需要捕获SIGINT、SIGTERM或任何表明命令即将退出的信号,以便通知worker进行资源清理。请问如何在Swift Package Target中实现该功能?
初始代码如下:
@main enum CLI { static func main() async throws { let worker = Worker() await worker.process() } }
实现方案
在Swift中可以通过GCD的DispatchSourceSignal来捕获POSIX信号,这种方式比传统的signal()函数更安全,适配异步代码环境。下面是具体实现步骤:
1. 给Worker添加可中断逻辑
让Worker支持接收中断通知,利用Swift Task的取消机制触发资源清理:
class Worker { private var processingTask: Task<Void, Error>? func process() async throws { processingTask = Task { // 模拟长期运行的数据处理、文件写入逻辑 while !Task.isCancelled { print("正在处理数据...") try await Task.sleep(nanoseconds: 1_000_000_000) } // 收到中断信号后执行清理 print("开始清理资源:关闭文件句柄、保存临时数据...") // 这里添加实际清理代码,比如关闭FileHandle、删除临时文件等 } try await processingTask?.value } func interrupt() { processingTask?.cancel() } }
2. 在CLI主逻辑中设置信号捕获
在main函数里创建信号源,监听目标信号并触发Worker的清理流程:
@main enum CLI { static func main() async throws { let worker = Worker() // 注册信号监听 setupSignalHandling(for: [SIGINT, SIGTERM]) { print("\n收到退出信号,准备清理...") worker.interrupt() } try await worker.process() print("程序正常退出") } private static func setupSignalHandling(for signals: [Int32], handler: @escaping () -> Void) { for signal in signals { let source = DispatchSource.makeSignalSource(signal: signal, queue: .main) source.setEventHandler(handler: handler) source.resume() // 绑定信号源引用,防止被ARC释放导致监听失效 objc_setAssociatedObject(self, "\(signal)", source, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } // 忽略系统默认信号处理,避免进程直接被终止 signal(SIGINT, SIG_IGN) signal(SIGTERM, SIG_IGN) } }
关键说明
- DispatchSourceSignal优势:GCD提供的线程安全信号监听方式,适配Swift异步代码场景,避免传统
signal()的线程安全问题。 - Task取消机制:通过
Task.isCancelled检测中断信号,让Worker优雅停止处理流程并执行资源清理。 - 信号源引用保留:必须通过关联对象或全局变量保留信号源实例,否则会被自动释放导致监听失效。
- 扩展信号支持:如果需要监听其他退出信号(如SIGHUP),直接将信号值添加到
signals数组即可。
内容的提问来源于stack exchange,提问作者Peacemoon
相关产品推荐
相关产品推荐

