Убедитесь, что запущен только один экземпляр программы - PullRequest
104 голосов
/ 19 декабря 2008

Есть ли у Pythonic способ запустить только один экземпляр программы?

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

(Примите во внимание, что иногда ожидается сбой программы, т.е. segfault - поэтому такие вещи, как "файл блокировки" не будут работать)

Обновление : предлагаемые решения намного сложнее и менее зависимы, чем просто порт, занятый несуществующим сервером, поэтому мне придется пойти с этим.

Ответы [ 19 ]

2 голосов
/ 23 сентября 2016

Вот мое возможное решение только для Windows. Поместите следующее в модуль, возможно, под названием «onlyone.py», или как угодно. Включите этот модуль непосредственно в ваш __ main __ файл сценария python.

import win32event, win32api, winerror, time, sys, os
main_path = os.path.abspath(sys.modules['__main__'].__file__).replace("\\", "/")

first = True
while True:
        mutex = win32event.CreateMutex(None, False, main_path + "_{<paste YOUR GUID HERE>}")
        if win32api.GetLastError() == 0:
            break
        win32api.CloseHandle(mutex)
        if first:
            print "Another instance of %s running, please wait for completion" % main_path
            first = False
        time.sleep(1)

Объяснение

Код пытается создать мьютекс с именем, полученным из полного пути к сценарию. Мы используем косую черту, чтобы избежать путаницы с реальной файловой системой.

Преимущества

  • Нет необходимости в настройках или «магических» идентификаторах, используйте их в любом количестве различных сценариев.
  • Нет старых файлов, мьютекс умирает вместе с вами.
  • Печатает полезное сообщение при ожидании
2 голосов
/ 24 апреля 2014

Я использую single_process на моем gentoo;

pip install single_process

пример :

from single_process import single_process

@single_process
def main():
    print 1

if __name__ == "__main__":
    main()   

см .: https://pypi.python.org/pypi/single_process/1.0

2 голосов
/ 02 декабря 2010

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

Решение Сорина Сбарни работает для меня под OS X, Linux и Windows, и я благодарен за это.

Тем не менее, tempfile.gettempdir () ведет себя одинаково в OS X и Windows, а другой - в некоторых / многие / все (?) * Никсах (игнорируя тот факт, что OS X также Unix!) Разница важна для этого кода.

OS X и Windows имеют пользовательские временные каталоги, поэтому временный файл, созданный одним пользователем, не виден другому пользователю. В отличие от этого, во многих версиях * nix (я тестировал Ubuntu 9, RHEL 5, OpenSolaris 2008 и FreeBSD 8) временный каталог / tmp для всех пользователей.

Это означает, что когда файл блокировки создается на многопользовательском компьютере, он создается в / tmp, и только пользователь, создавший файл блокировки в первый раз, сможет запустить приложение.

Возможное решение - вставить текущее имя пользователя в имя файла блокировки.

Стоит отметить, что решение OP по захвату порта также будет плохо работать на многопользовательском компьютере.

1 голос
/ 18 февраля 2019

Лучшее решение для этого в Windows - это использовать мьютексы, как предложено @ zgoda.

import win32event
import win32api
from winerror import ERROR_ALREADY_EXISTS

mutex = win32event.CreateMutex(None, False, 'name')
last_error = win32api.GetLastError()

if last_error == ERROR_ALREADY_EXISTS:
   print("App instance already running")

В некоторых ответах используется fctnl (также входит в пакет @sorin тендо), который недоступен в Windows, и если вы попытаетесь заморозить свое приложение на python, используя пакет типа pyinstaller, который выполняет статический импорт, он выдаст ошибку.

Кроме того, при использовании метода файла блокировки создается проблема read-only с файлами базы данных (возникает с sqlite3).

0 голосов
/ 17 января 2019

В системе Linux можно также спросить pgrep -a для количества экземпляров скрипта находится в списке процессов (опция -a показывает полная строка командной строки). Э.Г.

import os
import sys
import subprocess

procOut = subprocess.check_output( "/bin/pgrep -u $UID -a python", shell=True, 
                                   executable="/bin/bash", universal_newlines=True)

if procOut.count( os.path.basename(__file__)) > 1 :        
    sys.exit( ("found another instance of >{}<, quitting."
              ).format( os.path.basename(__file__)))

Удалить -u $UID, если ограничение должно применяться к всем пользователям. Отказ от ответственности: а) предполагается, что (базовое) имя сценария уникально, б) могут быть условия гонки.

0 голосов
/ 22 октября 2018
import sys,os

# start program
try:  # (1)
    os.unlink('lock')  # (2)
    fd=os.open("lock", os.O_CREAT|os.O_EXCL) # (3)  
except: 
    try: fd=os.open("lock", os.O_CREAT|os.O_EXCL) # (4) 
    except:  
        print "Another Program running !.."  # (5)
        sys.exit()  

# your program  ...
# ...

# exit program
try: os.close(fd)  # (6)
except: pass
try: os.unlink('lock')  
except: pass
sys.exit()  
0 голосов
/ 14 октября 2015

пример Linux

Этот метод основан на создании временного файла, автоматически удаляемого после закрытия приложения. при запуске программы мы проверяем наличие файла; если файл существует (есть ожидающее выполнение), программа закрывается; в противном случае он создает файл и продолжает выполнение программы.

from tempfile import *
import time
import os
import sys


f = NamedTemporaryFile( prefix='lock01_', delete=True) if not [f  for f in     os.listdir('/tmp') if f.find('lock01_')!=-1] else sys.exit()

YOUR CODE COMES HERE
0 голосов
/ 28 января 2015

Я столкнулся с этой проблемой на прошлой неделе, и хотя я нашел некоторые хорошие решения, я решил сделать очень простой и чистый пакет Python и загрузил его в PyPI. Он отличается от тендо тем, что может заблокировать любое имя строкового ресурса. Хотя вы, безусловно, могли бы заблокировать __file__ для достижения того же эффекта.

Установить с помощью: pip install quicklock

Использовать его чрезвычайно просто:

[nate@Nates-MacBook-Pro-3 ~/live] python
Python 2.7.6 (default, Sep  9 2014, 15:04:36)
[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.39)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from quicklock import singleton
>>> # Let's create a lock so that only one instance of a script will run
...
>>> singleton('hello world')
>>>
>>> # Let's try to do that again, this should fail
...
>>> singleton('hello world')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/nate/live/gallery/env/lib/python2.7/site-packages/quicklock/quicklock.py", line 47, in singleton
    raise RuntimeError('Resource <{}> is currently locked by <Process {}: "{}">'.format(resource, other_process.pid, other_process.name()))
RuntimeError: Resource <hello world> is currently locked by <Process 24801: "python">
>>>
>>> # But if we quit this process, we release the lock automatically
...
>>> ^D
[nate@Nates-MacBook-Pro-3 ~/live] python
Python 2.7.6 (default, Sep  9 2014, 15:04:36)
[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.39)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from quicklock import singleton
>>> singleton('hello world')
>>>
>>> # No exception was thrown, we own 'hello world'!

Взгляните: https://pypi.python.org/pypi/quicklock

0 голосов
/ 24 июля 2013

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

При запуске ваш процесс отправляет kill -0 всем процессам в определенной группе. Если такие процессы существуют, они завершаются. Затем он присоединяется к группе. Другие процессы не используют эту группу.

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

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

Полагаю, это не очень хорошее решение, разве кто-то может улучшить его?

...