Страницы

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

пятница, 10 января 2020 г.

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

#c_sharp


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

Пишу соответствующие методы:

  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().

Вопрос - так и надо делать или есть варианты проще?
    


Ответы

Ответ 1



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

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

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