如何从MemoryMappedFile获取WaitHandle?同名Mutex冲突问题求解
首先咱们来拆解你遇到的问题:
为什么同名会冲突?
Windows系统里,MemoryMappedFile和Mutex都是内核级对象,它们共享同一个全局命名空间。当你用同一个名称"tobyd"创建MMF之后,这个名称就被标记为属于“内存映射文件”类型的内核对象了。之后再尝试用同一个名称创建Mutex时,系统会发现这个名称已经被另一种类型的内核对象占用,直接报错;而Mutex.TryOpenExisting失败也是因为它要找的是Mutex类型的对象,但实际存在的是MMF,类型不匹配,自然找不到。
能不能从MemoryMappedFile获取WaitHandle?
很遗憾,MemoryMappedFile本身并没有直接提供WaitHandle供你等待它的释放。不过咱们可以自己搭配一个同步对象(比如EventWaitHandle或者Mutex),用不同的名称和MMF绑定,以此来跟踪MMF的生命周期。
如何等待MMF释放以复用名称?
这里有两种可行的思路:
1. 给MMF和同步对象用不同的命名(最推荐)
最简单的办法就是给MMF和Mutex分配不同的名称,彻底避免冲突。比如:
const string mmfName = "tobyd_MMF"; const string mutexName = "tobyd_Mutex"; // 创建或打开MMF using (MemoryMappedFile mmf = MemoryMappedFile.CreateOrOpen(mmfName, 64)) { // 创建同名Mutex?不,用单独的名称 using (Mutex mx = new Mutex(true, mutexName)) { // 执行你的业务逻辑 mx.ReleaseMutex(); } }
这种方法完全规避了命名冲突,代码也更清晰,是最稳妥的方案。
2. 跟踪MMF的释放状态,复用名称(如果一定要同名的话)
如果你坚持要复用同一个名称,那必须确保MMF的所有引用都被完全释放,系统销毁了对应的内核对象之后,才能创建同名的Mutex。这里可以用一个额外的EventWaitHandle来通知其他进程MMF已经释放:
进程A(创建MMF的进程):
const string targetName = "tobyd"; const string releaseEventName = "tobyd_Released"; // 创建释放通知事件 using (var releaseEvent = new EventWaitHandle(false, EventResetMode.AutoReset, releaseEventName)) using (MemoryMappedFile mmf = MemoryMappedFile.CreateOrOpen(targetName, 64)) { // 在这里使用你的内存映射文件... } // 当MMF的using块结束,它会被自动Dispose,此时触发通知事件 using (var releaseEvent = EventWaitHandle.OpenExisting(releaseEventName)) { releaseEvent.Set(); }
进程B(等待MMF释放后创建Mutex的进程):
const string targetName = "tobyd"; const string releaseEventName = "tobyd_Released"; try { // 等待MMF释放的通知 using (var releaseEvent = EventWaitHandle.OpenExisting(releaseEventName)) { releaseEvent.WaitOne(); } // 现在可以安全创建同名Mutex了 using (Mutex mx = new Mutex(true, targetName)) { // 执行你的Mutex操作 mx.ReleaseMutex(); } } catch (WaitHandleCannotBeOpenedException) { // 处理事件不存在的情况(比如进程A还没创建事件) }
需要注意的是:MMF被Dispose之后,系统可能不会立即销毁内核对象,直到所有关联的视图(比如MemoryMappedViewAccessor)也都被Dispose,所以确保你所有的MMF相关资源都在using块中管理,保证正确释放。
内容的提问来源于stack exchange,提问作者user3501292

