windows下用createmutex实现单实例锁最可靠,需检查getlasterror()==error_already_exists并使用global前缀命名;linux/macos推荐shm_open+flock文件锁;跨平台应封装singleinstanceguard类并注意uac、会话隔离等边界场景。

Windows 下用 CreateMutex 实现进程级单实例锁最直接
在 Windows 平台,CreateMutex 是最轻量、最可靠的方式——它由系统内核保证跨进程唯一性,且无需额外依赖。关键不是“创建成功”,而是“是否首次获得所有权”。
常见错误是只检查返回值是否为 NULL,却忽略 GetLastError() == ERROR_ALREADY_EXISTS 这个真正表示“已有实例在运行”的信号。
- 必须传入非
NULL的lpName字符串,建议用固定 GUID 或公司+产品名组合(如"Global\MyApp_7E2F1A8B-3C9D-4E1F-A0B1-C2D3E4F5A6B7"),避免命名冲突 - 推荐使用
Global\前缀,否则默认是会话级(Session-scoped),在多用户或服务环境下可能失效 - 调用后立即检查
GetLastError():若为ERROR_ALREADY_EXISTS,说明已有实例,当前进程应退出 - 不要忘记在主窗口关闭或程序退出前调用
CloseHandle,但即使不关,系统也会在进程终止时自动清理
HANDLE hMutex = CreateMutex(NULL, FALSE, TEXT("Global\MyApp_7E2F1A8B-3C9D-4E1F-A0B1-C2D3E4F5A6B7"));
if (hMutex == NULL || GetLastError() == ERROR_ALREADY_EXISTS) {
if (hMutex) CloseHandle(hMutex);
MessageBox(NULL, L"程序已在运行", L"提示", MB_ICONINFORMATION);
return 1;
}
Linux/macOS 下用 sem_open 或 shm_open + flock 更稳妥
POSIX 没有等价于 Windows CreateMutex 的原生命名互斥体,sem_open 看似合适,但实际存在严重坑点:某些旧版 glibc 不支持 SEM_FAILED 的正确返回,且信号量需手动 sem_close/sem_unlink,残留会导致后续启动失败。
更健壮的做法是用文件锁:shm_open 创建一个命名共享内存对象,再对其 fd 调用 flock 加锁。优势是语义清晰、无残留风险、兼容性好。
- 路径用
/tmp/myapp.lock即可,不必挂载tmpfs;flock对普通文件也完全有效 - 务必用
O_CREAT | O_RDWR打开,然后立刻flock(fd, LOCK_EX | LOCK_NB)——LOCK_NB避免阻塞,失败即说明已被占用 - 进程退出时
close(fd)即可释放锁,内核自动处理,不用显式unlock - 注意 umask 影响权限,建议
umask(0)后再 open,否则其他用户可能无法访问(尤其 daemon 场景)
跨平台封装要注意 std::string 与宽字符、路径分隔符的隐式转换
如果你用 C++17 写跨平台代码,别直接把 std::string 传给 Windows API。Win32 函数默认接受 LPCWSTR,而 std::string 是窄字符串,强制 reinterpret_cast 会乱码甚至崩溃。
- Windows 侧统一用
std::wstring存互斥名,构造时用TEXT()或L"xxx" - Linux/macOS 侧用
std::string,路径中一律用正斜杠/,不要硬写\ - 不要用
#ifdef _WIN32包裹整个逻辑,而是抽象出SingleInstanceGuard类,构造函数内部按平台分支实现 - 避免在锁判断之后、主窗口创建之前插入耗时操作(比如初始化日志、加载配置),否则可能被竞态绕过
容易被忽略的边界场景:UAC 提权、服务与桌面会话隔离、沙盒环境
很多“单实例失效”问题其实不出在代码,而出在运行上下文。比如以管理员身份运行的程序和普通用户启动的实例,即使名字相同,Windows 默认也不会视为同一个互斥体——因为它们处于不同完整性级别(IL)会话。
- 如果软件需要提权,互斥体名必须加
Global\前缀,并确保调用CreateMutex时不被 UAC 拦截(通常没问题,但某些安全策略会限制 Global 命名空间) - Windows 服务(Session 0)无法与用户桌面(Session 1+)共享
Global\互斥体,除非显式启用SeCreateGlobalPrivilege(不推荐) - 在 Steam、微信、Electron 沙盒或某些国产安全软件环境中,
CreateMutex可能被拦截或重定向,此时需 fallback 到基于本地 socket 或文件锁的方案 - macOS 上 sandboxed app(如 MAS 版本)禁止访问
/tmp,必须改用~/Library/Caches/下的路径
真正可靠的单实例机制,从来不是靠一个 API 调用,而是对运行环境有足够清醒的认知——名字只是入口,上下文才是决定成败的关键。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











