如何在C#中找到Mutex?

如何从C#中的互斥锁中找到获取互斥锁的?

mutex.WaitOne(timeout)超时时,它返回false 。 但是,如何从互斥锁手柄中找到它? (也许使用p / invoke。)

更新

 public class InterProcessLock : IDisposable { readonly Mutex mutex; public bool IsAcquired { get; private set; } public InterProcessLock(string name, TimeSpan timeout) { bool created; var security = new MutexSecurity(); security.AddAccessRule(new MutexAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null), MutexRights.Synchronize | MutexRights.Modify, AccessControlType.Allow)); mutex = new Mutex(false, name, out created, security); IsAcquired = mutex.WaitOne(timeout); } #region IDisposable Members public void Dispose() { if (IsAcquired) { mutex.ReleaseMutex(); IsAcquired = false; } } #endregion } 

目前,我使用自己的属性IsAcquired来确定是否应该释放互斥锁。 不是必要但更清楚,不是使用IsAcquired属性所代表的信息的二级副本,而是直接询问互斥锁是否被我获取。 因为调用mutex.ReleaseMutex()会抛出exception,如果我没有获取它。

(通过获取状态,我的意思是当我拥有互斥锁时,互斥锁处于未发出信号状态。)

(编辑:我添加了IsAcquired = false;感谢mattdekrey的post 。)

如您Mutex类上没有公共成员: http : //msdn.microsoft.com/en-us/library/system.threading.mutex_members.aspx

也没有公共本机function: http : //msdn.microsoft.com/en-us/library/ms686360%28v=VS.85%29.aspx

但是,有一些未记录/不受支持的函数,尤其是在ntdll.dll 。 这些允许访问系统对象。 但是,这些function可能会在未来版本的操作系统中更改或不可用。

因此,答案是:使用传统方法是不可能的。

没有干净方法的原因是因为这不是一个好主意,原因是因为当你依赖这种类型的逻辑时,竞争条件很容易引入。 所以你的设计需要改变。

首先,您不应该在构造函数中获取锁。 将此类转换为返回正确初始化的互斥对象的工厂。 这样你就可以知道你是否获得了锁。

不要依赖Dispose来释放锁,这就是要求难以维护的死锁代码。 使用try / finally块确保它被释放。

超时有点粗略。 仅在未获取锁定时使用超时将被视为正常操作。 无法获取锁通常是一个错误,只是通过超时避免它隐藏了错误。 如果您需要超时,请考虑使用事件(可能是AutoResetEvent),这可能更合适。

你为什么不能使用Mutex.OpenExisting

 try { Mutex foundMutex = Mutex.OpenExisting("MyTestingMutex"); // Found Mutex foundMutex.ReleaseMutex(); } catch (System.Threading.WaitHandleCannotBeOpenedException) { // System.Threading.WaitHandleCannotBeOpenedException: // The named mutex does not exist. } 

编辑

我猜其中一些。

您似乎正在尝试开发API。 您在API中提供的项目之一是InterProcessLock。

我将假设您正在跨线程共享一个集合,并且您正在使用Mutex来确保一次只有一个操作。

 using (InterProcessLock myLock = new InterProcessLock("LockMutex", TimeSpan.FromMilliseconds(100.0))) { if(myLock.IsAcquired) { // I have control then I can delete, add to the collection. } } 

我会重新考虑这个设计。 如果我从未在使用中包装InterProcessLock myLock = new InterProcessLock("LockMutex", TimeSpan.FromMilliseconds(100.0))怎么办? 不会调用Dispose。 如果用户根本不打电话给Dispose怎么办?

会有一个被遗弃的Mutex

来自MSDN

注意废弃的互斥锁通常表示代码中存在严重错误。 当线程退出但未释放互斥锁时,受互斥锁保护的数据结构可能不处于一致状态。 如果可以validation数据结构的完整性,请求互斥锁所有权的下一个线程可以处理此exception并继续。

如果您试图保护您的用户,您可能希望通过为他们控制互斥锁来帮助他们,这样他们就不必担心它。

一个可能的例子是

 public static bool PerformLockedProcess(Action process, string commonLockName, TimeSpan timeout) { Mutex mutex = null; // Get the Mutex for the User try { bool created; var security = new MutexSecurity(); security.AddAccessRule(new MutexAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null), MutexRights.Synchronize | MutexRights.Modify, AccessControlType.Allow)); mutex = new Mutex(false, commonLockName, out created, security); bool acquired = mutex.WaitOne(timeout); if (acquired) { process(); return true; } return false; } finally { // Make sure we do not abandon the Mutex if (mutex != null) { try { mutex.ReleaseMutex(); } catch (ApplicationException) { // In case that failes } } } } 

这是一种可能的方式。 这一切都取决于目标是什么。 因为Mutex是一个操作系统构造,所以我不会在最终用户上转发来调用Dispose。 如果名称不是unquie,它可能会影响使用相同互斥锁名称的其他进程。

嗯,这不完全是你所要求的,但我认为它可以解决你的问题:为什么不只是添加一些error handling专门针对在其他人获得Mutex时发生的exception?

 public void Dispose() { if (IsAcquired) try { mutex.ReleaseMutex(); } catch (System.Threading.SynchronizationLockException) { // Handle the exception, assuming you need to do anything. // All other exceptions would still be passed up the stack. } } 

这不会有益于问题的原始海报,但在这里。

虽然我不同意其他正确使用互斥锁的海报,但我有一个应用程序,我需要测试某人是否拥有互斥锁而没有自己拥有所有权。 正如其他人所提到的,唯一的方法是使用来自ntdll.dll的未记录的NtQueryMutant系统调用。 我为Mutex类创建了一个扩展方法,可以像这样使用:

  bool createdNew = true; var m = new Mutex(false, MutexName, out createdNew); if ( m != null) { int currentCount; bool ownedByCaller, abandonedState; if (m.TryQuery(out currentCount, out ownedByCaller, out abandonedState)) { Console.WriteLine(string.Format("Created New: {3}, Count: {0}, OwvedByMe: {1}, Abandoned: {2}", currentCount, ownedByCaller, abandonedState, createdNew)); } m.Close(); } 

这是实施

 public static class MutexExtensionMethods { public static bool TryQuery(this Mutex m, out int currentCount, out bool ownedByCaller, out bool abandonedState) { currentCount = -1; ownedByCaller = abandonedState = false; try { var handle = m.SafeWaitHandle; if (handle != null) { var h = handle.DangerousGetHandle(); MutantBasicInformation mbi; int retLength; var ntStatus = NtQueryMutant( h, MutantInformationClass.MutantBasicInformation, out mbi, Marshal.SizeOf(typeof(MutantBasicInformation)), out retLength); GC.KeepAlive(handle); // Prevent "handle" from being collected before NtQueryMutant returns if (ntStatus == 0) { currentCount = mbi.CurrentCount; ownedByCaller = mbi.OwnedByCaller; abandonedState = mbi.AbandonedState; return true; } } } catch { } return false; } #region NTDLL.DLL [DllImport("ntdll.dll")] public static extern uint NtQueryMutant( [In] IntPtr MutantHandle, [In] MutantInformationClass MutantInformationClass, [Out] out MutantBasicInformation MutantInformation, [In] int MutantInformationLength, [Out] [Optional] out int ReturnLength ); public enum MutantInformationClass : int { MutantBasicInformation } [StructLayout(LayoutKind.Sequential)] public struct MutantBasicInformation { public int CurrentCount; [MarshalAs(UnmanagedType.U1)] public bool OwnedByCaller; [MarshalAs(UnmanagedType.U1)] public bool AbandonedState; } #endregion } 

.NET Mutex类是本机互斥包装器,它提供与本机互斥API相同的可能性(除了等待不同类型的可等待对象的数量)。 如果要在不阻塞的情况下获取互斥锁,请调用mutex.WaitOne(0)。 使用PInvoke,您可以调用WaitForSingleObject,结果相同。

如果您真的想要进行进程间锁定,顾名思义,您将需要一种方法来检测Mutex是否实际上已被获取,是否正确? 如果没有IsAcquired属性,我不确定如何确保使用InterProcessLock代码被锁定。 (另外,为了防止意外调用Dispose两次的程序员,我在Dispose方法IsAcquired设置为false 。)

我自己实现了同样的事情(因为我更喜欢使用块来尝试 – 最后只是为了释放互斥锁)而是在超过超时时抛出exception,如果我正确地记住了项目,那么不要调用Dispose方法。

编辑:添加了在构造函数中抛出exception的好处:你的关键部分也完全避免了,你可以在catch块中执行error handling,无论如何,这可能包括你的关键部分调用的方法,尽管我个人会考虑这是一种不好的做法。

经过进一步思考,而不是使用另一个答案中指定的try ... catch ,您可以在处置时使用以下内容:

 public void Dispose() { if (IsAcquired) { lock (mutex) { mutex.ReleaseMutex(); IsAcquired = false; } } } 

lock互斥lock感觉有点讽刺,但你有它。 虽然我完全同意你不应该依赖于因使用IDisposable接口的文档而调用Dispose,但我认为using() { }块指示进程间关键部分是非常方便的。