Предотвращение многократных идентичных процессов от нереста [дубликат]

Если у вас есть набор файлов, которые вы всегда будете проверять, вы можете псевдонимы их путей, например:

alias fd='find . -type f -regex ".*\.\(inc\|info\|module\|php\|test\|install\|uninstall\)"'

Затем вы можете просто отфильтровать список следующим образом:

grep -U -l $'\015' $(fd)

Отфильтровывает список fd для файлов, содержащих шаблон CR.

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

49
задан Mike Christensen 4 September 2014 в 02:41
поделиться

9 ответов

В документации объясняется (в разделе «Замечания»), что существует концептуальная разница между экземпляром объекта Mutex (который делает не , фактически, делать что-либо особенное насколько синхронизация идет) и получение Mutex (с использованием WaitOne ). Обратите внимание, что:

  • WaitOne возвращает логическое значение, что означает, что получение Mutex может сбой (время ожидания), и оба случая должны быть обработаны
  • Когда WaitOne возвращает true, тогда вызывающий поток приобрел Mutex и должен вызывать ReleaseMutex, иначе Mutex перестанет
  • Когда он вернет false ], то вызывающий поток не должен вызывать ReleaseMutex

Итак, для мьютексов больше, чем для создания экземпляра. Что касается того, следует ли использовать using в любом случае, давайте посмотрим, что делает Dispose (поскольку унаследовано от WaitHandle ):

protected virtual void Dispose(bool explicitDisposing)
{
    if (this.safeWaitHandle != null)
    {
        this.safeWaitHandle.Close();
    }
}

Как мы можем см., Mutex выпущен не , но есть какая-то очистка, поэтому прилипание с using было бы хорошим подходом.

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

Если вы действительно не заботятся о случае, когда Mutex не может быть приобретен (что вы не указали, поскольку вы проходите мимо a TimeSpan - WaitOne), вы можете обернуть Mutex в свой класс, который реализует IDisposable, получить Mutex в конструкторе (используя WaitOne() без аргументов) и выпустить его внутри Dispose. Хотя, я бы, вероятно, не рекомендовал бы это, так как это заставило бы ваши потоки ждать бесконечно, если что-то пойдет не так, и независимо от того, есть веские причины для явной обработки обоих случаев при попытке приобретения, как упомянуто @HansPassant.

49
ответ дан Community 22 August 2018 в 22:31
поделиться
  • 1
    +1 - Отличный ответ! Я думаю, что тот факт, что вы не можете вызывать ReleaseMutex, если WaitOne failed, означает, что Dispose не может реализовать это, если только он не реализовал логику для отслеживания успешных ожиданий , Похоже, решение состоит в том, чтобы поместить Mutex в блок использования и поместить блок try/finally в блок if. – Mike Christensen 21 August 2014 в 19:20
  • 2
    @MikeChristensen он мог бы просто throw в случае, если ему не удалось получить мьютекс в своем тайм-ауте, что в любом случае имеет смысл ИМО, поскольку это указывает на сбой. – Benjamin Gruenbaum 15 September 2014 в 09:42

Dispose зависит от выхода WaitHandle. Таким образом, хотя using вызывает Dispose, он не будет вдаваться в силу до тех пор, пока не будут выполнены условия стабильного состояния. Когда вы вызываете ReleaseMutex, вы сообщаете системе, что вы освобождаете ресурс, и, таким образом, он может свободно распоряжаться им.

1
ответ дан B.K. 22 August 2018 в 22:31
поделиться

Это дизайнерское решение было принято давно, давным-давно. Более 21 года назад, задолго до появления .NET, или когда-либо рассматривалась семантика IDisposable. Класс .NET Mutex является классом обертки для базовой поддержки операционной системы для мьютексов. Конструктор pinvokes CreateMutex , метод WaitOne () pinvokes WaitForSingleObject () .

Обратите внимание на возвращаемое WAIT_ABANDONED значение WaitForSingleObject (), это тот, который генерирует исключение.

Дизайнеры Windows поместили твердое правило, в котором поток, который владеет мьютексом, должен вызывать ReleaseMutex () до его выхода. И если это не значит, что это очень сильное указание на то, что поток неожиданно завершен, как правило, через исключение. Это означает, что синхронизация потеряна, очень серьезная проблема с потоками. Сравните с Thread.Abort (), очень опасным способом прервать поток в .NET по той же причине.

Разработчики .NET никоим образом не изменяли это поведение. Не в последнюю очередь потому, что нет никакого способа проверить состояние мьютекса, кроме как выполнить ожидание. Вы должны вызывать ReleaseMutex (). И обратите внимание, что ваш второй фрагмент не является правильным; вы не можете называть это мьютексом, который вы не получили. Он должен быть перемещен внутри тела оператора if ().

34
ответ дан Hans Passant 22 August 2018 в 22:31
поделиться
  • 1
    +1 - Спасибо за историю! Можете ли вы прокомментировать код, который я разместил в моем ответе , на этот вопрос? Из того, что я могу сказать, это должен быть идеальный способ сделать это. – Mike Christensen 21 August 2014 в 19:44
  • 2
    Хмья, ты прикрываешь диагностику, которую специально разработали дизайнеры Windows. Ваш код разбился, когда он владел мьютексом, вы больше не можете рассуждать о состоянии программы. Шансы, которые вы можете восстановить, чрезвычайно тонкие, особенно потому, что у вас нет другого достойного способа оповестить другой процесс о том, что он должен прекратить использовать общий ресурс, поскольку он находится в совершенно неизвестном состоянии. Исключения - хорошая вещь, не делайте этого. – Hans Passant 21 August 2014 в 19:50
  • 3
    Итак, лучше всего просто разрешить ему сбой и отказаться от Mutex? Затем, пусть другой WaitOne вызывает также крах? – Mike Christensen 21 August 2014 в 20:05
  • 4
    Вы говорите, что разработчики .NET ничего не изменили, но я, кажется, помню, что исключение AbandonedMutexException, которое было выбрано WaitXXX, было изменением с 1.1 до 2.0. В разделе 1.1 (и 1.0) обертки метода WaitXXX только что вернулись, если они столкнулись с оставленным мьютексом. И много развлечений и игр. – Damien_The_Unbeliever 22 August 2014 в 07:18
  • 5
    @Damien_The_Unbeliever: Концепция блокирующих примитивов, имеющих «ожидаемое условие», никогда не произойдет ». состояние очень полезно. Я хочу, чтобы блокировки монитора включали что-то подобное, хотя я не уверен, что существующий код можно было бы ожидать, если кто-то выбросил один из его замков в такое состояние. Есть веские причины, по которым Monitor.Pulse можно сделать только после приобретения блокировки, но должен быть механизм, посредством которого код, который выключает услугу, может устанавливать свои мониторы на «выключение», без необходимости приобретать связанные блокировки. Если что-то вроде финализатора ... – supercat 22 August 2014 в 17:12

Нам нужно понять, что больше .net, чтобы знать, что происходит в начале страницы MSDN, дает первый намек на то, что кто-то «нечетно» происходит:

Примитив синхронизации, который также может использоваться для межпроцессной синхронизации.

Mutex - это «Именованный объект» Win32, каждый процесс блокирует его по имени, объект .net - это всего лишь оболочка вокруг вызовов Win32. Сам Muxtex живет в адресном пространстве Windows Kernal, а не в адресном пространстве вашего приложения.

В большинстве случаев вам лучше использовать монитор , если вы только пытаетесь синхронизировать доступ к объектам в рамках одного процесса.

4
ответ дан Ian Ringrose 22 August 2018 в 22:31
поделиться

Если вам нужно гарантировать, что мьютекс выпущен, переключитесь на блок finally try catch и поместите релиз мьютекса в блок finally. Предполагается, что вы владеете и имеете ручку для мьютекса. Эта логика должна быть включена до вызова release.

3
ответ дан Mike Beeler 22 August 2018 в 22:31
поделиться
  • 1
    Что вы имеете в виду? Означает ли это, что выражение using отличается от try/finally{Dispose()}? – Sriram Sakthivel 21 August 2014 в 18:57
  • 2
    @SriramSakthivel, нет, это означает, что вам нужно использовать try/finally для вызова ReleaseMutex, а не только Dispose (что делается с помощью инструкции using). – CMircea 21 August 2014 в 19:00
  • 3
    Это решение не будет работать, поскольку оно выдает исключение в блоке finally, если время вызова WaitOne истекает. – Mike Christensen 21 August 2014 в 19:30

Хорошо, отправляя ответ на мой собственный вопрос. Из того, что я могу сказать, , этот является идеальным способом реализации Mutex, который:

  1. Всегда получает Disposed
  2. Получает освобождение iff WaitOne был успешным.
  3. Не будет отказано, если какой-либо код выдает исключение.

Надеюсь, это поможет кому-то!

using (Mutex mut = new Mutex(false, MUTEX_NAME))
{
    if (mut.WaitOne(new TimeSpan(0, 0, 30)))
    {
        try
        {
           // Some code that deals with a specific TCP port
           // Don't want this to run twice in multiple processes        
        }
        catch(Exception)
        {
           // Handle exceptions and clean up state
        }
        finally
        {
            mut.ReleaseMutex();
        }
    }
}

Обновление. Некоторые могут утверждать, что если код в блоке try помещает ваш ресурс в нестабильное состояние, вы должны not отпустить Mutex и вместо этого оставить его заброшенным. Другими словами, просто позвоните mut.ReleaseMutex();, когда код завершится успешно, а не поместите его в блок finally. Код, приобретающий Mutex, мог бы поймать это исключение, а сделать правильную вещь .

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

7
ответ дан Mike Christensen 22 August 2018 в 22:31
поделиться
  • 1
    Не знаю, почему это было приостановлено, но я добавил несколько подробностей. Возможно, это просто считается плохая форма , чтобы ответить на ваш собственный вопрос. – Mike Christensen 21 August 2014 в 23:21
  • 2
    Неплохая форма, чтобы ответить на ваш собственный вопрос вообще! Вероятно, это было отклонено, потому что код неверен. Вы ловите и глотаете исключение, которое скрывает серьезную проблему с потоками - такую ​​ошибку, которую вы не хотите под ковриком. – Cody Gray♦ 22 August 2014 в 10:42
  • 3
    @CodyGray Я вижу, что Майк добавляет предложение catch после вашего комментария. Но не блок try / finally, без catch(){}, хорошо: исключение может обрабатываться выше стека, а не проглотить? (И если его вообще не поймать, программа завершается, и не имеет значения, выпуская мьютекс!) – Darren Cook 20 November 2014 в 12:03

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

  1. Получить мьютекс
  2. Внести изменения в объект, из-за которого его состояние становится недействительным
  3. Внесите изменения в объект, из-за чего его состояние снова станет действительным
  4. Отпустите мьютекс

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

Шаблон, который несколько лучше, чем тот, который реализован объектом mutex .NET, заключается в том, чтобы метод «приобретать» возвращал объект IDisposable который инкапсулирует не мьютекс, а скорее его частное приобретение. Утилизация этого объекта приведет к отмене мьютекса. Код может выглядеть примерно так:

using(acq = myMutex.Acquire())
{
   ... stuff that examines but doesn't modify the guarded resource
   acq.EnterDanger();
   ... actions which might invalidate the guarded resource
   ... actions which make it valid again
   acq.LeaveDanger();
   ... possibly more stuff that examines but doesn't modify the resource
}

Если внутренний код не работает между EnterDanger и LeaveDanger, тогда объект сбора должен аннулировать мьютекс, вызывая Dispose на нем, поскольку охраняемый ресурс может находиться в поврежденном состоянии. Если внутренний код выходит из строя в другом месте, мьютекс должен быть освобожден, поскольку охраняемый ресурс находится в допустимом состоянии, а код в блоке using больше не понадобится. У меня нет каких-либо конкретных рекомендаций библиотек, реализующих этот шаблон, но это не особенно сложно реализовать в качестве обертки вокруг других видов мьютексов.

7
ответ дан supercat 22 August 2018 в 22:31
поделиться

Читая документацию для ReleaseMutex, кажется, что конструктивное решение заключалось в том, что Mutex следует освобождать сознательно. если ReleaseMutex не вызывается, это означает аномальный выход защищенной секции. сдача релиза в конечном итоге или уничтожение, обходит этот механизм. вы по-прежнему можете игнорировать исключение AbandonedMutex, конечно.

2
ответ дан ths 22 August 2018 в 22:31
поделиться

Знайте: Mutex.Dispose (), выполняемый сборщиком мусора, терпит неудачу, потому что процесс сбора мусора не принадлежит дескриптору в соответствии с Windows.

2
ответ дан Willem-Derk Nijdam 22 August 2018 в 22:31
поделиться
Другие вопросы по тегам:

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