Czy istnieje sposób w Pythonie, aby uruchomić tylko jedną instancję programu?
Jedynym rozsądnym rozwiązaniem, które wymyśliłem, jest próba uruchomienia go jako serwera na jakimś porcie, a następnie drugi program próbujący połączyć się z tym samym portem - kończy się niepowodzeniem. Ale to naprawdę nie jest dobry pomysł, może jest coś lżejszego niż to?
(Weź pod uwagę, że czasami oczekuje się, że program zawiedzie, np. Segfault - więc rzeczy takie jak „lock file” nie będą działać)
python
process
locking
mutual-exclusion
Slava V
źródło
źródło
Odpowiedzi:
Poniższy kod powinien wykonać zadanie, jest wieloplatformowy i działa na Pythonie 2.4-3.2. Przetestowałem to na Windowsie, OS X i Linuksie.
Najnowsza wersja kodu jest dostępna na singleton.py . Prosimy o zgłaszanie błędów tutaj .
Możesz zainstalować program za pomocą jednej z następujących metod:
easy_install tendo
pip install tendo
źródło
Prosty,
wieloplatformowerozwiązanie, znalezione w innym pytaniu przez Zgoda :Bardzo podobne do sugestii S. Lotta, ale z kodem.
źródło
fcntl
systemie Windows nie ma modułu (chociaż funkcjonalność może być emulowana).fg
. Wygląda więc na to, że działa poprawnie (tj. Aplikacja jest nadal aktywna, ale zawieszona, więc blokada pozostaje na miejscu).lock_file_pointer = os.open(lock_path, os.O_WRONLY | os.O_CREAT)
Ten kod jest specyficzny dla systemu Linux. Używa „abstrakcyjnych” gniazd domeny UNIX, ale jest prosty i nie pozostawia starych plików blokady. Wolę to od rozwiązania powyżej, ponieważ nie wymaga specjalnie zarezerwowanego portu TCP.
Unikalny ciąg
postconnect_gateway_notify_lock
można zmienić, aby zezwolić na wiele programów, które wymagają wymuszenia jednej instancji.źródło
Nie wiem, czy jest wystarczająco pythonowy, ale w świecie Javy nasłuchiwanie na określonym porcie jest dość szeroko stosowanym rozwiązaniem, ponieważ działa na wszystkich głównych platformach i nie ma żadnych problemów z zawieszaniem się programów.
Kolejną zaletą nasłuchiwania portu jest możliwość wysłania polecenia do działającej instancji. Na przykład, gdy użytkownicy uruchamiają program po raz drugi, możesz wysłać działającej instancji polecenie, aby nakazała jej otwarcie innego okna (na przykład to robi Firefox. Nie wiem, czy używają portów TCP, nazwanych potoków lub coś w tym rodzaju '').
źródło
import socket; s = socket.socket(socket.AF_INET, socket.SOCK_STREAM); s.bind(('localhost', DEFINED_PORT))
. AnOSError
zostanie podniesiony, jeśli inny proces jest powiązany z tym samym portem.Nigdy wcześniej nie pisałem Pythona, ale właśnie to zaimplementowałem w mycheckpoint, aby zapobiec uruchamianiu go dwa lub więcej razy przez crond:
Znalazłem sugestię Slava-N po opublikowaniu tego w innym numerze (http://stackoverflow.com/questions/2959474). Ta jest wywoływana jako funkcja, blokuje plik wykonywanych skryptów (nie plik pid) i utrzymuje blokadę do zakończenia skryptu (normalny lub błąd).
źródło
Użyj pliku pid. Masz jakąś znaną lokalizację, „/ path / to / pidfile” i podczas uruchamiania robisz coś takiego (częściowo pseudokod, ponieważ przygotowuję kawę i nie chcę tak ciężko pracować):
Innymi słowy, sprawdzasz, czy istnieje plik pidfile; jeśli nie, zapisz swój pid do tego pliku. Jeśli plik pid istnieje, sprawdź, czy pid jest numerem pid działającego procesu; jeśli tak, to masz uruchomiony inny proces na żywo, więc po prostu zamknij. Jeśli nie, to poprzedni proces się zawiesił, więc zaloguj się, a następnie zapisz swój własny pid w pliku zamiast starego. Następnie kontynuuj.
źródło
Znalazłeś już odpowiedź na podobne pytanie w innym wątku, więc ze względu na kompletność zobacz, jak osiągnąć to samo w systemie Windows bez nazwy mutex.
http://code.activestate.com/recipes/474070/
źródło
To może zadziałać.
Spróbuj utworzyć plik PID w znanej lokalizacji. Jeśli ci się nie uda, ktoś zablokował plik - gotowe.
Kiedy skończysz normalnie, zamknij i usuń plik PID, aby ktoś inny mógł go zastąpić.
Możesz opakować swój program w skrypt powłoki, który usuwa plik PID nawet w przypadku awarii programu.
Możesz także użyć pliku PID do zabicia programu, jeśli się zawiesi.
źródło
Używanie pliku blokady jest dość powszechnym podejściem na Uniksie. Jeśli się zawiesza, musisz wyczyścić ręcznie. Możesz zapisać PID w pliku i przy uruchomieniu sprawdzić, czy istnieje proces z tym PID, nadpisując plik blokady, jeśli nie. (Jednak potrzebujesz również blokady wokół pliku read-file-check-pid-rewrite-file). W pakiecie os znajdziesz to, czego potrzebujesz do pobrania i sprawdzenia pid . Powszechnym sposobem sprawdzenia, czy istnieje proces z danym pidem, jest wysłanie mu nie-fatalnego sygnału.
Inną alternatywą mogłoby być połączenie tego z semaforami typu flock lub Posix.
Otwarcie gniazda sieciowego, zgodnie z propozycją saua, byłoby prawdopodobnie najłatwiejsze i najbardziej przenośne.
źródło
Każdy, kto używa wxPython w swojej aplikacji, może skorzystać z funkcji
wx.SingleInstanceChecker
opisanej tutaj .Osobiście używam podklasy,
wx.App
która korzysta zwx.SingleInstanceChecker
i wracaFalse
z,OnInit()
jeśli istnieje już instancja aplikacji, która jest już wykonywana w następujący sposób:Jest to prosty zamiennik typu drop-in,
wx.App
który uniemożliwia wiele wystąpień. Aby z niego skorzystać wystarczy wymienićwx.App
zeSingleApp
w kodzie tak:źródło
Oto moje ostateczne rozwiązanie tylko dla systemu Windows. Wstaw poniższy kod do modułu, być może o nazwie „onlyone.py” lub jakikolwiek inny. Dołącz ten moduł bezpośrednio do swojego __ głównego __ pliku skryptu języka Python.
Wyjaśnienie
Kod próbuje utworzyć muteks o nazwie pochodzącej z pełnej ścieżki do skryptu. Używamy ukośników, aby uniknąć potencjalnego pomylenia z rzeczywistym systemem plików.
Zalety
źródło
Najlepszym rozwiązaniem w przypadku okien jest użycie muteksów, zgodnie z sugestią @zgoda.
Niektóre odpowiedzi używają
fctnl
(zawarte również w pakiecie @sorin tendo), które nie jest dostępne w systemie Windows i jeśli spróbujesz zamrozić aplikację Pythona za pomocą pakietu, takiego jakpyinstaller
import statyczny, generuje błąd.Ponadto użycie metody pliku blokującego stwarza
read-only
problem z plikami bazy danych (doświadczony w przypadkusqlite3
).źródło
Publikuję to jako odpowiedź, ponieważ jestem nowym użytkownikiem, a Stack Overflow nie pozwala mi jeszcze głosować.
Rozwiązanie Sorin Sbarnea działa dla mnie pod OS X, Linux i Windows i jestem za to wdzięczny.
Jednak tempfile.gettempdir () zachowuje się w jeden sposób pod OS X i Windows, a w inny pod niektórymi / many / all (?) * Nixami (ignorując fakt, że OS X jest również Uniksem!). Różnica jest ważna w tym kodzie.
Systemy OS X i Windows mają katalogi tymczasowe specyficzne dla użytkownika, więc plik tymczasowy utworzony przez jednego użytkownika nie jest widoczny dla innego użytkownika. Z kolei w wielu wersjach * nix (testowałem Ubuntu 9, RHEL 5, OpenSolaris 2008 i FreeBSD 8), katalog tymczasowy to / tmp dla wszystkich użytkowników.
Oznacza to, że gdy plik blokujący jest tworzony na komputerze z wieloma użytkownikami, jest tworzony w / tmp i tylko użytkownik, który utworzył plik blokujący po raz pierwszy, będzie mógł uruchomić aplikację.
Możliwym rozwiązaniem jest osadzenie aktualnej nazwy użytkownika w nazwie pliku blokady.
Warto zauważyć, że rozwiązanie OP polegające na przechwytywaniu portu będzie również źle działać na maszynie wielodostępnej.
źródło
Używam
single_process
na moim gentoo;przykład :
patrz: https://pypi.python.org/pypi/single_process/1.0
źródło
Podejrzewam, że powinno być dobre rozwiązanie POSIXy wykorzystujące grupy procesów, bez konieczności uderzania w system plików, ale nie mogę tego całkiem ustalić. Coś jak:
Podczas uruchamiania proces wysyła komunikat „kill -0” do wszystkich procesów w określonej grupie. Jeśli istnieją takie procesy, kończy się. Następnie dołącza do grupy. Żadne inne procesy nie używają tej grupy.
Jednak jest to warunek wyścigu - wiele procesów może to zrobić dokładnie w tym samym czasie i wszystkie ostatecznie dołączają do grupy i działają jednocześnie. Do czasu dodania jakiegoś muteksu, aby uczynić go wodoszczelnym, nie potrzebujesz już grup procesów.
Może to być akceptowalne, jeśli twój proces jest uruchamiany tylko przez crona, raz na minutę lub co godzinę, ale denerwuje mnie trochę, że pójdzie źle dokładnie w dniu, w którym tego nie chcesz.
Wydaje mi się, że to jednak nie jest zbyt dobre rozwiązanie, chyba że ktoś może to poprawić?
źródło
W zeszłym tygodniu napotkałem dokładnie ten problem i chociaż znalazłem kilka dobrych rozwiązań, zdecydowałem się stworzyć bardzo prosty i czysty pakiet Pythona i załadowałem go do PyPI. Różni się od tendo tym, że może zablokować dowolną nazwę zasobu ciągu. Chociaż z pewnością można by było zablokować,
__file__
aby osiągnąć ten sam efekt.Zainstaluj za pomocą:
pip install quicklock
Korzystanie z niego jest niezwykle proste:
Spójrz: https://pypi.python.org/pypi/quicklock
źródło
Opierając się na odpowiedzi Roberto Rosario, wymyślam następującą funkcję:
Musimy zdefiniować global
SOCKET
vaiable, ponieważ będzie on zbierany jako śmieci dopiero po zakończeniu całego procesu. Jeśli zadeklarujemy zmienną lokalną w funkcji, wyjdzie ona poza zakres po zakończeniu funkcji, a tym samym gniazdo zostanie usunięte.Wszystko to zasługa Roberto Rosario, ponieważ ja tylko wyjaśniam i rozwijam jego kod. Ten kod będzie działał tylko w Linuksie, jak wyjaśnia poniższy cytowany tekst z https://troydhanson.github.io/network/Unix_domain_sockets.html :
źródło
przykład linux
Ta metoda polega na utworzeniu pliku tymczasowego automatycznie usuwanego po zamknięciu aplikacji. po uruchomieniu programu weryfikujemy istnienie pliku; jeśli plik istnieje (oczekuje na wykonanie), program jest zamykany; w przeciwnym razie tworzy plik i kontynuuje wykonywanie programu.
źródło
W systemie Linux można również zapytać
pgrep -a
o liczbę instancji, skrypt znajduje się na liście procesów (opcja -a ujawnia pełny ciąg wiersza poleceń). Na przykładUsuń,
-u $UID
jeśli ograniczenie ma dotyczyć wszystkich użytkowników. Zastrzeżenie: a) zakłada się, że (bazowa) nazwa skryptu jest unikalna, b) mogą istnieć warunki wyścigu.źródło
źródło