Как найти, что Взаимное исключение в C# получено?

Как я могу найти от взаимоисключающего дескриптора в 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() выдает исключение, если оно не получено мной.

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

(РЕДАКТИРОВАНИЕ: Я добавил IsAcquired = false; благодаря сообщению mattdekrey.)

21
задан Community 23 May 2017 в 12:32
поделиться

6 ответов

Как вы могли обнаружить, в классе 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. Они позволяют получить доступ к системным объектам. Однако эти функции могут измениться или быть недоступными в будущих версиях операционной системы.

Итак, ответ: это невозможно с помощью обычных средств.

5
ответ дан 29 November 2019 в 21:47
поделиться

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

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

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

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

13
ответ дан 29 November 2019 в 21:47
поделиться

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

0
ответ дан 29 November 2019 в 21:47
поделиться

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

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

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

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

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

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

0
ответ дан 29 November 2019 в 21:47
поделиться

Ну, это не совсем то, о чем вы просите, но я думаю, это решит вашу проблему: почему бы просто не добавить обработку ошибок специально для исключения, которое возникает, если мьютекс переходит к кому-то другому?

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.
        }
}
2
ответ дан 29 November 2019 в 21:47
поделиться

Почему вы не можете использовать 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)) в using? Dispose не будет называться. Что, если пользователь вообще никогда не вызывает Dispose?

Будет брошен Mutex

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

2
ответ дан 29 November 2019 в 21:47
поделиться
Другие вопросы по тегам:

Похожие вопросы: