如何在Unity Windows构建的应用中使用named mutex?有无可行方案
Unity Windows 构建使用命名Mutex的解决方案
问题根本原因
- Unity编辑器运行时采用完整Mono运行时,原生支持.NET的
System.Threading.Mutex类,包括命名Mutex的跨进程同步逻辑 - Windows构建版本如果使用IL2CPP后端,.NET API兼容裁剪会限制命名Mutex的默认权限;同时打包后的应用默认运行在低权限上下文,无法访问全局命名空间的Mutex,部分Mono后端打包场景也会受安全配置限制出现兼容问题
可行解决方法
方法1:调整Mutex初始化参数,显式指定权限
这是改造成本最低的兼容方案,默认构造的命名Mutex会默认申请全局命名空间权限,打包后的应用无对应权限,修改为显式声明局部命名空间+自定义权限规则即可适配:
using System.Threading; using System.Security.AccessControl; using System.Security.Principal; private Mutex _appMutex; private bool _isNewInstance; void Awake() { // 配置允许所有用户访问的Mutex权限规则 var mutexSecurity = new MutexSecurity(); mutexSecurity.AddAccessRule(new MutexAccessRule( new SecurityIdentifier(WellKnownSidType.WorldSid, null), MutexRights.FullControl, AccessControlType.Allow)); // 第二个参数为自定义Mutex名称,不要加"Global\"前缀,默认用"Local\"前缀即可适配打包后权限 _appMutex = new Mutex(true, "YourAppUniquePrefix_MutexName", out _isNewInstance, mutexSecurity); if (!_isNewInstance) { // 已有实例运行时执行对应逻辑,比如单实例场景下直接退出 Application.Quit(); return; } } void OnDestroy() { // 应用退出时必须主动释放Mutex句柄 if (_appMutex != null) { _appMutex.ReleaseMutex(); _appMutex.Dispose(); } }
注:如果用Mono后端打包,可直接省略权限参数运行,IL2CPP后端必须显式指定权限规则才能正常创建命名Mutex
方法2:调用Windows原生API创建Mutex
如果.NET封装的Mutex类仍然不生效,可以直接通过P/Invoke调用Windows系统原生CreateMutexW接口,完全绕过Unity运行时的API裁剪限制:
using System.Runtime.InteropServices; using System; public class Win32Mutex { [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] private static extern IntPtr CreateMutexW(IntPtr lpMutexAttributes, bool bInitialOwner, string lpName); [DllImport("kernel32.dll", SetLastError = true)] private static extern bool ReleaseMutex(IntPtr hMutex); [DllImport("kernel32.dll", SetLastError = true)] private static extern bool CloseHandle(IntPtr hObject); [DllImport("kernel32.dll", SetLastError = true)] private static extern uint GetLastError(); private const uint ERROR_ALREADY_EXISTS = 183; private IntPtr _mutexHandle = IntPtr.Zero; public bool IsNewInstance { get; private set; } public Win32Mutex(string mutexName, bool initialOwner = true) { _mutexHandle = CreateMutexW(IntPtr.Zero, initialOwner, mutexName); uint errorCode = GetLastError(); IsNewInstance = errorCode != ERROR_ALREADY_EXISTS; } public void Release() { if (_mutexHandle != IntPtr.Zero) { ReleaseMutex(_mutexHandle); } } public void Dispose() { if (_mutexHandle != IntPtr.Zero) { CloseHandle(_mutexHandle); _mutexHandle = IntPtr.Zero; } } }
该实现IL2CPP/Mono后端完全兼容,不需要额外配置权限,只要Mutex名称符合当前应用的权限等级要求即可正常运行。
注意事项
- 命名Mutex的名称要保证全局唯一,避免和其他应用的Mutex重名,建议前缀带上你的应用包名/唯一标识
- 如果用于单实例启动逻辑,要在应用启动最早阶段初始化Mutex,比如在首个场景的Awake周期初始化,或者用
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]属性提前初始化 - 应用退出时一定要主动释放Mutex句柄,否则可能出现Mutex被意外占用的异常
内容的提问来源于stack exchange,提问作者JJCodesFresh
相关产品推荐
相关产品推荐

