Как запретить нескольким процессам создавать несколько экземпляров другого процесса?

#python #concurrency #race-condition

#python #параллелизм #состояние гонки

Вопрос:

У меня есть 2 процесса: Start и Status. Одновременно может выполняться несколько процессов запуска, и должен быть только 1 экземпляр процесса состояния.

При запуске процесса запуска он попытается запустить статус. На данный момент я пытаюсь остановить запуск нескольких статусов, заставляя процесс Status проверять, был ли привязан порт сервера Status, чтобы определить, существует ли другой статус, и если да, то он завершится корректно.

Однако у этого есть условие гонки, когда во время проверки привязанного порта может быть другой статус, который выполнил эту проверку и находится в процессе привязки этого порта, следовательно, будет создано 2 статуса.

Есть ли решение для этого на уровне процесса? Я рассматривал возможность использования другого процесса, отслеживающего количество статусов в системе, но есть ли другой подход?

Редактировать: это делается в Python 2.6

Edit2: как Start, так и Status извлекаются из оболочки.

Ответ №1:

Во-первых, вы можете использовать что-то вроде supervisord для координации выполнения процессов. На самом деле это неплохо. Вы в основном настраиваете свои процессы, максимальное число, которое может выполняться, И т. Д., И Он будет обрабатывать все остальное. Однако вам нужно разобраться со всеми небольшими элементами конфигурации. Вы также можете выделить этот процесс состояния в отдельный, если он выполняется как поток в другом процессе. Таким образом, вы добьетесь большего успеха.

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

 try: 
    open_port_listener()
except socket.error as e:
    do_nothing()
 

Или, может быть, ввести немного более детальную проверку типа ошибки сокета. Я предполагаю, что вы используете socket и получаете socket.error (возможно, адрес уже используется или что-то в этом роде?).

Если вы запускали это с помощью multiprocessing модуля (который, как я знаю, вы не используете, поскольку вы уточнили, но я оставлю это на всякий случай, если это будет полезно для кого-либо еще), вы могли бы использовать блокировку, чтобы гарантировать, что проверка состояния процесса происходит в том или ином процессе. multiprocessing Модуль поддерживает это. Прочитайте документы здесь.

 from multiprocessing import Process, Lock

def startStatusProcess(l):
    l.acquire()    
    if not do_check():
        Process(target=runStatusProcess, args=()).start()
    l.release() 

if __name__ == '__main__':
    lock = Lock()
    startStatusProcess(lock)
 

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

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

2. В принципе, acquire() выдаст блокировку. Если второй процесс попытается выполнить попытку acquire() после первого, он заблокирует это утверждение до тех пор, пока не возникнут проблемы с первым процессом release() . Это предотвращает выполнение проверки и выполнения процесса параллельно с проверкой и выполнением другого процесса. Просто имейте в виду, это должен быть один и тот же Lock() объект.

3. Кроме того, я предполагал, что вы используете multiprocessing модуль. Если это не так, и вы запускаете несколько исполнений из оболочки, тогда я бы выбрал что-то вроде supervisord .

4. Запуск и статус выполняются из оболочки, извините за отсутствие этой детали в OP. Я обновлю его.

5. Да, я пытаюсь привязать, а затем перехватить ошибку сокета, чтобы узнать, используется ли она уже. Проблема в том, что я должен использовать другой обязательный сторонний уровень tcp / ip, поэтому, если он успешно привязан, я должен отменить привязку, а затем выполнить вызовы этого стороннего уровня. На этом этапе может возникнуть состояние гонки.

Ответ №2:

Вероятно, существуют способы pythonic, но я бы использовал супервизор процесса, такой как daemontools, systemd, runit и т. Д., Для запуска и контроля процесса состояния, чтобы убедиться, что он один и только один.