macOS LaunchAgent重复进程:如何实现单用户单进程?
刚好之前处理过类似的场景,给你整理几个用Launchd原生配置就能搞定的方案,从根源上避免同一用户下出现重复进程:
1. 先把Label设成唯一的
每个LaunchAgent的Label是Launchd识别任务的唯一标识,一定要用反向域名这种全局唯一的格式(比如com.yourcompany.yourapp),别随便起个名字。只要Label唯一,Launchd默认就不会重复启动同一个任务——这是最基础的前提,先确认你的plist里这部分没写错。
2. 开启SingleInstance强制单实例(macOS 10.10+)
从Yosemite开始,Launchd加了个SingleInstance的布尔键,直接把它设为true,Launchd就会严格保证同一个Label的任务只能跑一个实例。哪怕因为竞态条件触发了两次加载请求,它也会直接忽略第二次。
在你的plist里加这段配置:
<key>SingleInstance</key> <true/>
3. 别手动加载和自动加载撞车
如果你的应用需要用户登录就自动启动,直接把RunAtLoad设为true:
<key>RunAtLoad</key> <true/>
这种情况下,你完全不用手动执行launchctl load——Launchd会自动监控~/Library/LaunchAgents或/Library/LaunchAgents目录,一旦plist被放进去就自动加载,而且会自己管理实例,不会出现重复启动的问题。
如果确实需要手动加载,记得用launchctl load -w命令,加-w参数会把任务标记为持久化,Launchd会记住这个状态,不会重复加载同一个任务。
4. 给UI应用加会话限制
因为你跑的是UI应用,加上LimitLoadToSessionType指定只能在Aqua会话(就是用户的图形桌面会话)里运行,避免在其他非图形会话里意外启动:
<key>LimitLoadToSessionType</key> <string>Aqua</string>
极端情况的兜底:应用层加单实例检查
要是以上配置还碰到极端竞态(概率很低),可以在应用代码里加一层检查:
- 用
NSWorkspace的runningApplications方法,启动时检查有没有自己的实例已经在跑; - 或者在
~/Library/Application Support/YourApp/目录下创建一个锁文件,启动时先看锁文件存在不,存在就直接退出。
内容的提问来源于stack exchange,提问作者Zohar81

