简体   繁体   English

如何在C#中找到Mutex?

[英]How to find that Mutex in C# is acquired?

How can I find from mutex handle in C# that a mutex is acquired? 如何从C#中的互斥锁中找到获取互斥锁的?

When mutex.WaitOne(timeout) timeouts, it returns false . mutex.WaitOne(timeout)超时时,它返回false However, how can I find that from the mutex handle? 但是,如何从互斥锁手柄中找到它? (Maybe using p/invoke.) (也许使用p / invoke。)

UPDATE : 更新

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
}

Currently, I am using my own property IsAcquired to determine whether I should release a mutex. 目前,我使用自己的属性IsAcquired来确定是否应该释放互斥锁。 Not essential but clearer, would be not to use a secondary copy of the information represented by IsAcquired property, but rather to ask directly the mutex whether it is acquired by me. 不是必要但更清楚,不是使用IsAcquired属性所代表的信息的二级副本,而是直接询问互斥锁是否被我获取。 Since calling mutex.ReleaseMutex() throws an exception if it is not acquired by me. 因为调用mutex.ReleaseMutex()会抛出异常,如果我没有获取它。

(By acquired state I mean that the mutex is in not-signaled state when I am owning the mutex.) (通过获取状态,我的意思是当我拥有互斥锁时,互斥锁处于未发出信号状态。)

(EDIT: I have added IsAcquired = false; thanks to mattdekrey's post .) (编辑:我添加了IsAcquired = false;感谢mattdekrey的帖子 。)

The reason there is no clean way to do this is because it is not a good idea and the reason is because race conditions are -very- easy to introduce when you rely on this type of logic. 没有干净方法的原因是因为这不是一个好主意,原因是当你依赖这种类型的逻辑时,竞争条件很容易引入。 So your design needs to change. 所以你的设计需要改变。

First, you should not acquire a lock in a constructor. 首先,您不应该在构造函数中获取锁。 Turn this class into a factory that returns a properly initialized mutex object. 将此类转换为返回正确初始化的互斥对象的工厂。 That way you can know if you acquired the lock or not. 这样你就可以知道你是否获得了锁。

DO NOT rely on Dispose to release locks, this is asking for deadlock ridden code that is hard to maintain. 不要依赖Dispose释放锁,这就是要求死锁的代码很难维护。 Use a try/finally block to ensure it is released. 使用try / finally块确保它被释放。

Timeouts are a bit sketchy. 超时有点粗略。 Only use timeouts when not acquiring the lock would be considered normal operation. 仅在未获取锁定时使用超时将被视为正常操作。 Not being able to acquire the lock is usually a bug and merely avoiding it with timeouts hides the bug. 无法获取锁通常是一个错误,只是通过超时避免它隐藏了错误。 If you need timeouts, consider using an event (maybe AutoResetEvent), this may be more appropriate. 如果您需要超时,请考虑使用事件(可能是AutoResetEvent),这可能更合适。

As you may found, there are no public members on Mutex class: http://msdn.microsoft.com/en-us/library/system.threading.mutex_members.aspx 您可能已经发现, Mutex类上没有公共成员: http//msdn.microsoft.com/en-us/library/system.threading.mutex_members.aspx

There is also no public native functions for that: http://msdn.microsoft.com/en-us/library/ms686360%28v=VS.85%29.aspx 也没有公共本机功能: http//msdn.microsoft.com/en-us/library/ms686360%28v=VS.85%29.aspx

However, there are some undocumented/unsupported functions especially in ntdll.dll . 但是,有一些未记录/不支持的功能,尤其是在ntdll.dll These allow accessing system objects. 这些允许访问系统对象。 However, these functions may change or not be available in future versions of operating system. 但是,这些功能可能会在未来版本的操作系统中更改或不可用。

So, the answer is: It is not possible using conventional means. 因此,答案是:使用传统方法是不可能的。

Well, it's not exactly what you're asking for, but I think it would solve your problem: why not just add some error handling specifically for the exception that occurs if the Mutex is aquired by someone else? 嗯,这不完全是你要求的,但我认为它可以解决你的问题:为什么不只是添加一些错误处理专门针对发生的异常,如果Mutex被别人获得?

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

Why can't you use Mutex.OpenExisting 你为什么不能使用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.
}

EDIT 编辑

I am guessing some of this. 我猜其中一些。

It seems like you are trying to develop an API. 您似乎正在尝试开发API。 One of the items you are offering in your API is an InterProcessLock. 您在API中提供的项目之一是InterProcessLock。

I am going to assume you are sharing a collection across threads and you are using the Mutex to make sure only one operation is on it a time. 我将假设您正在跨线程共享一个集合,并且您正在使用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.
    }
}

I would reconsider this design. 我会重新考虑这个设计。 What if I never wraped InterProcessLock myLock = new InterProcessLock("LockMutex", TimeSpan.FromMilliseconds(100.0)) in a using? 如果我从来没有在使用中包装InterProcessLock myLock = new InterProcessLock("LockMutex", TimeSpan.FromMilliseconds(100.0))怎么办? Dispose would not be called. 不会调用Dispose。 What if the user never calls the Dispose at all? 如果用户根本不打电话给Dispose怎么办?

There would be an abandoned Mutex 会有一个被遗弃的Mutex

From MSDN 来自MSDN

Caution An abandoned mutex often indicates a serious error in the code. 注意废弃的互斥锁通常表示代码中存在严重错误。 When a thread exits without releasing the mutex, the data structures protected by the mutex might not be in a consistent state. 当线程退出但未释放互斥锁时,受互斥锁保护的数据结构可能不处于一致状态。 The next thread to request ownership of the mutex can handle this exception and proceed, if the integrity of the data structures can be verified. 如果可以验证数据结构的完整性,则请求互斥锁所有权的下一个线程可以处理此异常并继续。

If you are trying to protect your users you might want to help them by controlling the Mutex for them so they never have to worry about it. 如果您试图保护您的用户,您可能希望通过为他们控制互斥锁来帮助他们,这样他们就不必担心它。

A possible example is 一个可能的例子是

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

This is one possible way. 这是一种可能的方式。 It all depends on what the goal is. 这一切都取决于目标是什么。 I would NOT relay on the final user to call a Dispose since a Mutex is an operating system construct. 因为Mutex是一个操作系统构造,所以我不会在最终用户上中继调用Dispose。 And if the name is not unquie it could effect other processes using the same mutex name. 如果名称不是unquie,则可能会影响使用相同互斥锁名称的其他进程。

This won't benefit the original poster of the question but here it goes. 这不会有益于问题的原始海报,但在这里。

While I do not disagree with other posters on proper use of Mutexes, I had an application where I needed to test whether someone owns a mutex without taking ownership myself. 虽然我不同意其他正确使用互斥锁的海报,但我有一个应用程序,我需要测试某人是否拥有互斥锁而没有自己拥有所有权。 As mentioned by others the only way is to use an undocumented NtQueryMutant system call from ntdll.dll. 正如其他人所提到的,唯一的方法是使用来自ntdll.dll的未记录的NtQueryMutant系统调用。 I created an extension method for the Mutex class that can be used like this: 我为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();
        }

And here is the implementation 这是实施

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 class is native mutex wrapper, which gives the same possibilities, as native mutex API (except waiting for number of waitable objects of different type). .NET Mutex类是本机互斥包装器,它提供与本机互斥API相同的可能性(除了等待不同类型的可等待对象的数量)。 If you want to acquire mutex without blocking, call mutex.WaitOne(0). 如果要在不阻塞的情况下获取互斥锁,请调用mutex.WaitOne(0)。 Using PInvoke, you can call WaitForSingleObject, with the same result. 使用PInvoke,您可以调用WaitForSingleObject,结果相同。

If you're really trying to do an inter-process lock, as the name implies, you will want a way to detect if the Mutex has actually been acquired anyway, correct? 如果您真的想要进行进程间锁定,顾名思义,您将需要一种方法来检测Mutex是否实际上已被获取,是否正确? I'm not sure how your code that uses your InterProcessLock would be ensured to be locked if there was no IsAcquired property. 如果没有IsAcquired属性,我不确定如何确保使用InterProcessLock代码被锁定。 (Also, to protect against programmers who accidentally call Dispose twice, I'd set the IsAcquired to false in your Dispose method.) (另外,为了防止意外调用Dispose两次的程序员,我在Dispose方法IsAcquired设置为false 。)

I've implemented the same thing myself (because I much prefer the using block to a try-finally just to release the mutex) and instead threw an exception when the timeout was exceeded, which, if I'm remembering the project correctly, did not call the Dispose method. 我自己实现了同样的事情(因为我更喜欢使用块来尝试 - 最后只是为了释放互斥锁)而是在超出超时时抛出异常,如果我正确地记住了项目,那么不要调用Dispose方法。

Edit: Added benefit of throwing the exception in the constructor: your critical section is also completely avoided, and you can perform error handling in the catch block, which could include the same method call your critical section had, anyway, though I personally would consider that a bad practice. 编辑:添加了在构造函数中抛出异常的好处:你的关键部分也完全避免了,你可以在catch块中执行错误处理,无论如何,这可能包括你的关键部分调用相同的方法,尽管我个人会考虑这是一种不好的做法。

Upon further reflection, rather than using try ... catch as specified in another answer, you could use the following on your dispose: 经过进一步思考,而不是使用另一个答案中指定的try ... catch ,您可以在处置时使用以下内容:

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

It feels a little ironic to lock a mutex, but there you have it. lock互斥lock感觉有点讽刺,但你有它。 While I totally agree that you shouldn't rely on Dispose being called because of the documentation with the IDisposable interface, I think it is incredibly convenient to have an inter-process critical section indicated by a using() { } block. 虽然我完全同意你不应该依赖于因使用IDisposable接口的文档而调用Dispose,但我认为using() { }块指示进程间关键部分是非常方便的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM