Страницы

Поиск по вопросам

понедельник, 25 февраля 2019 г.

Как правильно занимать и отпускать Mutex?

Нужно, с одной стороны, обеспечить запуск единственного экземпляра приложения, с другой - в тестах надо имитировать запуск и выключение приложения.
Пишу соответствующие методы:
public static class Client { private static Mutex mutex;
public static void Start() { var isSingle = false; mutex = new Mutex(false, guid, out isSingle); if (!isSingle) { Log.Add("Программа уже запущена."); Environment.Exit(1); } }
public static void Close() { if (mutex != null && !mutex.SafeWaitHandle.IsClosed) { Log.Add("Wait mutex..."); mutex.WaitOne(); mutex.ReleaseMutex(); mutex.Close(); } } }
Если создание и проверка в целом выглядят понятно, то вот с закрытием явные проблемы. По синтаксису хотелось вызвать просто ReleaseMutex, ему потребовался WaitOne, а потом оказалось, что ReleaseMutex не закрывает хендл и при следущем тесте старт уже невозможен, для чего пришлось добавлять ещё и Close()
Вопрос - так и надо делать или есть варианты проще?


Ответ

Вы пытаетесь использовать сразу два разных свойства мьютекса:
Уникальность именнованного мьютекса в системе Возможность захватить его только одному клиенту одновременно
Первое свойство характерно не только для мьютексов, но для всех глобальных объектов синхронизации. Суть его в том, что в системе может существовать только один объект синхронизации с определенным именем.
Попытка создать такой же объект откроет уже существующий (созданный в другом процессе), но при этом явно вам на это укажет - возвратом параметраcreatedNew == false.
Мьютекс существует до тех пор, пока его хэндл не будет закрыт во всех процессах. Т.е. createdNew (ваш isSingle) будет выставлен в false в случае, если такой мьютекс уже где-то (в другом экземпляре приложения) существует.
Т.е. сам по себе механизм предотвращения повторного запуска опирается исключительно на свойство единственности существования именованного Mutex, а не на возможность эксклюзивно его захватить.
Методы WaitOne, ReleaseMutex и первый параметр конструктора - initiallyOwned используются для эксклюзивного захвата, и в вашем случае совершенно не нужны.
Минимальный код в вашем случае выглядит так:
public static class Client { private static Mutex mutex;
public static void Start() { var createdNew = false; mutex = new Mutex(false, "someid", out createdNew); if (!createdNew) { Console.WriteLine("Программа уже запущена."); Environment.Exit(1); } }
public static void Close() { if (mutex != null && !mutex.SafeWaitHandle.IsClosed) { Console.WriteLine("Close handle"); mutex.Close(); } } }
Чтобы проверить, что сам по себе факт того, что вы используете именно Mutex, не влияет на результат, можете попробовать заменить его на что-то другое, например семафор с максимальным лимитом в 100:
new Semaphore(0, 100, "someid", out createdNew)
В отличии от мьютекса, его могут захватить до 100 раз. Но, как и в случае мьютекса, сама возможность захвата не используется - и он точно так же будет предотвращать запуск второго экземпляра - за счет уникальности имени.

Комментариев нет:

Отправить комментарий