代码之家  ›  专栏  ›  技术社区  ›  TN.

如何找到C中的互斥体?

  •  18
  • TN.  · 技术社区  · 14 年前

    如何从C中的mutex句柄中找到获取mutex的原因?

    什么时候? 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 以确定是否应释放互斥。不重要,但更清楚的是,将不会使用由 IS-获得的 属性,而不是直接询问互斥体是否由我获取。打电话以来 mutex.ReleaseMutex() 如果不是我获得的,则引发异常。

    (由 获得 我的意思是互斥体在 未发出信号 我的状态 拥有 互斥体)

    (编辑:我已添加 IsAcquired = false; 多亏了 mattdekrey's post )

    7 回复  |  直到 8 年前
        1
  •  4
  •   lioil    14 年前

    正如您可能发现的,没有公开成员 Mutex 班级: http://msdn.microsoft.com/en-us/library/system.threading.mutex_members.aspx

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

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

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

        2
  •  13
  •   Karl Strings    14 年前

    没有干净的方法可以做到这一点的原因是因为这不是一个好主意,原因是当你依赖于这种逻辑时,比赛条件很容易引入。所以你的设计需要改变。

    首先,不应获取构造函数中的锁。将这个类转换为一个返回正确初始化的互斥对象的工厂。这样你就能知道你是否拿到了锁。

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

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

        3
  •  2
  •   David Basarab    14 年前

    为什么你不能用 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中提供的项目之一是进程间锁。

    我假设您正在跨线程共享一个集合,并且您使用互斥来确保一次只对它执行一个操作。

    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呢?

    会有一个废弃的互斥体

    MSDN

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

    如果你试图保护你的用户,你可能想通过控制他们的互斥来帮助他们,这样他们就不用担心了。

    一个可能的例子是

    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
                }
            }
        }
    }
    

    这是一种可能的方法。这完全取决于目标是什么。由于互斥体是操作系统构造,所以我不会中继最终用户来调用Dispose。如果名称不是非静态的,它可能会影响使用相同互斥名称的其他进程。

        4
  •  1
  •   Mike U    14 年前

    好吧,这不是你想要的,但我认为它可以解决你的问题:为什么不专门为互斥被其他人捕获时发生的异常添加一些错误处理呢?

    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.
            }
    }
    
        5
  •  1
  •   Paul Groke Chronial    8 年前

    这不会对问题的原始海报有好处,但这里是。

    虽然我不反对其他关于正确使用互斥体的海报,但是我有一个应用程序,我需要在那里测试某人是否拥有互斥体而不占用自己的所有权。正如其他人提到的,唯一的方法是使用来自ntdll.dll的未记录的ntqueryvariant系统调用。我为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
    
    }
    
        6
  •  0
  •   Alex F    14 年前

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

        7
  •  0
  •   Matt DeKrey Kiran    14 年前

    如果你真的想做一个进程间锁,顾名思义,你需要一种方法来检测互斥锁是否真的被获取了,对吗?我不确定您的代码如何使用 InterProcessLock 如果没有 IsAcquired 财产。(另外,为了防止程序员意外调用Dispose两次,我将设置 IS-获得的 false 在你 Dispose 方法。

    我自己也实现了同样的事情(因为我更喜欢使用块,而不是尝试最终释放互斥体),而是在超时时抛出异常,如果我正确记住项目,则不会调用Dispose方法。

    编辑: 在构造函数中引发异常的额外好处是:还完全避免了关键部分,并且可以在 catch 块,它可以包含相同的方法调用您的关键部分,无论如何,尽管我个人会认为这是一个糟糕的实践。

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

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

    感觉有点讽刺 lock 一个互斥体,但你有它。虽然我完全同意您不应该依赖于使用IDisposable接口的文档来调用Dispose,但我认为使用 using() { } 块。