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