Подтвердить что ты не робот

Как найти, что Mutex в С# получен?

Как я могу найти из mutex handle в С#, что мьютекс получен?

Когда mutex.WaitOne(timeout) таймауты, он возвращает false. Однако, как я могу найти это из дескриптора мьютекса? (Возможно, используя 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
}

В настоящее время я использую собственное свойство IsAcquired, чтобы определить, должен ли я выпустить мьютекс. Не важно, но яснее, было бы не использовать вторичную копию информации, представленной свойством IsAcquired, а скорее запрашивать непосредственно мьютекс, независимо от того, приобретена ли она мной. Поскольку вызов mutex.ReleaseMutex() выдает исключение, если он не получен мной.

(По полученному состоянию я имею в виду, что мьютекс находится в состоянии без сигнализации, когда я владею мьютексом.)

(EDIT: я добавил IsAcquired = false; благодаря сообщению mattdekrey.)

4b9b3361

Ответ 1

Как вы можете обнаружить, в классе 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

Причина, по которой нет чистого способа сделать это, состоит в том, что это не очень хорошая идея, и причина в том, что условия гонки - очень легко представить, когда вы полагаетесь на этот тип логики. Поэтому ваш дизайн должен измениться.

Во-первых, вы не должны приобретать блокировку в конструкторе. Превратите этот класс в factory, который возвращает правильно инициализированный объект mutex. Таким образом, вы можете узнать, приобрели ли вы замок или нет.

НЕ полагайтесь на Dispose, чтобы освобождать блокировки, это требует кода, который трудно поддерживать. Используйте блок try/finally для обеспечения его освобождения.

Тайм-ауты немного отрывочны. Использовать таймауты, когда не приобретать блокировку, будет считаться нормальной работой. Невозможность получить блокировку обычно является ошибкой и просто избегает ее с тайм-аутами, скрывает ошибку. Если вам нужны таймауты, подумайте об использовании события (возможно, AutoResetEvent), это может быть более целесообразным.

Ответ 3

Почему вы не можете использовать 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

Внимание Заброшенный мьютекс часто указывает на серьезную ошибку в коде. Когда поток выходит без освобождения мьютекса, структуры данных, защищенные мьютексом, могут не находиться в согласованном состоянии. Следующий поток для запроса права собственности на мьютекс может справиться с этим исключением и продолжить, если целостность структур данных может быть проверена.

Если вы пытаетесь защитить своих пользователей, вы можете помочь им, контролируя Mutex для них, поэтому им никогда не придется беспокоиться об этом.

Возможный пример:

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, поскольку Mutex - это конструкция операционной системы. И если имя не является неопределенным, это может повлиять на другие процессы, используя одно и то же имя мьютекса.

Ответ 4

Ну, это не совсем то, о чем вы просите, но я думаю, что это решит вашу проблему: почему бы просто не добавить некоторую обработку ошибок специально для исключения, которое возникает, если 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.
        }
}

Ответ 5

Это не принесет пользу оригинальному плакату вопроса, но здесь он идет.

Пока я не согласен с другими плакатами по правильному использованию мьютексов, у меня было приложение, в котором мне нужно было проверить, владеет ли кто-то мьютексом, не взяв на себя ответственность. Как упоминалось другими, единственный способ - использовать недокументированный системный вызов NtQueryMutant из ntdll.dll. Я создал метод расширения для класса 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

.NET Mutex class - это встроенная оболочка mutex, которая дает те же возможности, что и собственный API-интерфейс mutex (за исключением ожидания количества ожидаемых объектов разного типа). Если вы хотите получить мьютекс без блокировки, вызовите mutex.WaitOne(0). Используя PInvoke, вы можете вызвать WaitForSingleObject с тем же результатом.

Ответ 7

Если вы действительно пытаетесь сделать блокировку между процессами, как следует из названия, вам понадобится способ определить, действительно ли Mutex действительно был получен, правильно? Я не уверен, что ваш код, который использует ваш InterProcessLock, будет заблокирован, если не было свойства IsAcquired. (Кроме того, чтобы защитить от программистов, которые случайно вызвали Dispose дважды, я установил IsAcquired в false в ваш метод Dispose.)

Я реализовал то же самое сам (потому что я предпочитаю использовать блок для try-finally, просто для выпуска мьютекса) и вместо этого выбрасывал исключение, когда превышен тайм-аут, который, если я помню проект правильно, не вызывал метод Dispose.

Edit: Добавленное преимущество бросания исключения в конструкторе: ваш критический раздел также полностью исключается, и вы можете выполнять обработку ошибок в блоке catch, который может включать тот же метод, что и ваш критический раздел, во всяком случае, хотя я лично буду рассматривать что плохая практика.

При дальнейшем отражении вместо использования try ... catch, как указано в другом ответе, вы можете использовать следующее на своем распоряжении:

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

Чувствуется немного ироничным для lock мьютекса, но там у вас оно есть. Хотя я полностью согласен с тем, что вы не должны полагаться на вызов Dispose из-за документации с интерфейсом IDisposable, я думаю, что невероятно удобно иметь критический раздел между процессами, обозначенный блоком using() { }.