Stellen Sie sicher, dass nur eine einzige Instanz eines Programms ausgeführt wird

119

Gibt es eine pythonische Möglichkeit, nur eine Instanz eines Programms auszuführen?

Die einzige vernünftige Lösung, die ich mir ausgedacht habe, ist der Versuch, sie als Server an einem Port auszuführen. Das zweite Programm, das versucht, an denselben Port zu binden, schlägt fehl. Aber es ist keine wirklich gute Idee, vielleicht gibt es etwas Leichteres als dieses?

(Berücksichtigen Sie, dass das Programm manchmal fehlschlägt, dh Segfault - Dinge wie "Datei sperren" funktionieren also nicht)

Slava V.
quelle
1
Vielleicht wäre Ihr Leben einfacher, wenn Sie den Segfault aufspüren und beheben würden. Nicht, dass es einfach wäre.
David Locke
Es ist nicht in meiner Bibliothek, es ist in Pythons libxml-Bindungen und extrem schüchtern - wird nur einmal in ein paar Tagen ausgelöst.
Slava V
5
Die Standardbibliothek von Python unterstützt flock (), das Richtige für moderne UNIX-Programme. Beim Öffnen eines Ports wird ein Punkt in einem viel engeren Namespace verwendet, während PID-Dateien komplexer sind, da Sie laufende Prozesse überprüfen müssen, um sie sicher ungültig zu machen. Herde hat kein Problem.
Charles Duffy
s / UNIX / linux / los geht's, FTFY.
Kaleissin

Antworten:

100

Der folgende Code sollte die Aufgabe erfüllen, er ist plattformübergreifend und läuft unter Python 2.4-3.2. Ich habe es unter Windows, OS X und Linux getestet.

from tendo import singleton
me = singleton.SingleInstance() # will sys.exit(-1) if other instance is running

Die neueste Codeversion ist singleton.py verfügbar . Bitte melden Sie hier Fehler .

Sie können tend mit einer der folgenden Methoden installieren:

Sorin
quelle
2
Ich habe die Antwort aktualisiert und einen Link zur neuesten Version eingefügt. Wenn Sie einen Fehler finden, senden Sie ihn bitte an github und ich werde ihn so schnell wie möglich beheben.
Sorin
2
@ Johny_M Danke, ich habe einen Patch gemacht und eine neuere Version auf pypi.python.org/pypi/tendo veröffentlicht
sorin
2
Diese Syntax funktionierte unter Windows 2.6 unter Windows nicht. Was für mich funktionierte war: 1: von Tendo Import Singleton 2: Me = Singleton.SingleInstance ()
Brian
25
Eine weitere Abhängigkeit für etwas so Triviales wie dieses? Klingt nicht sehr attraktiv.
WhyNotHugo
2
Behandelt der Singleton Prozesse, die einen Sigterm erhalten (z. B. wenn ein Prozess zu lange läuft), oder muss ich das verarbeiten?
JimJty
43

Einfache plattformübergreifende Lösung, gefunden in einer anderen Frage von zgoda :

import fcntl
import os
import sys

def instance_already_running(label="default"):
    """
    Detect if an an instance with the label is already running, globally
    at the operating system level.

    Using `os.open` ensures that the file pointer won't be closed
    by Python's garbage collector after the function's scope is exited.

    The lock will be released when the program exits, or could be
    released if the file pointer were closed.
    """

    lock_file_pointer = os.open(f"/tmp/instance_{label}.lock", os.O_WRONLY)

    try:
        fcntl.lockf(lock_file_pointer, fcntl.LOCK_EX | fcntl.LOCK_NB)
        already_running = False
    except IOError:
        already_running = True

    return already_running

Sehr ähnlich wie S.Lotts Vorschlag, aber mit dem Code.

Slava V.
quelle
Aus Neugier: Ist das wirklich plattformübergreifend? Funktioniert es unter Windows?
Joachim Sauer
1
Unter fcntlWindows gibt es kein Modul (obwohl die Funktionalität emuliert werden könnte).
JFS
10
TIPP: Wenn Sie dies in eine Funktion einschließen möchten, muss 'fp' global sein, sonst wird die Datei nach dem Beenden der Funktion geschlossen.
cmcginty
1
@Mirko Control + Z beendet eine Anwendung nicht (auf jedem mir bekannten Betriebssystem), sondern setzt sie aus. Die Anwendung kann mit in den Vordergrund zurückgesetzt werden fg. Es hört sich also so an, als ob es für Sie richtig funktioniert (dh die App ist noch aktiv, aber angehalten, sodass die Sperre bestehen bleibt).
Sam Bull
1
Dieser Code in meiner Situation (Python 3.8.3 unter Linux) musste modifiziert werden:lock_file_pointer = os.open(lock_path, os.O_WRONLY | os.O_CREAT)
baziorek
30

Dieser Code ist Linux-spezifisch. Es verwendet 'abstrakte' UNIX-Domänensockets, ist jedoch einfach und hinterlässt keine veralteten Sperrdateien. Ich ziehe es der obigen Lösung vor, da es keinen speziell reservierten TCP-Port erfordert.

try:
    import socket
    s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
    ## Create an abstract socket, by prefixing it with null. 
    s.bind( '\0postconnect_gateway_notify_lock') 
except socket.error as e:
    error_code = e.args[0]
    error_string = e.args[1]
    print "Process already running (%d:%s ). Exiting" % ( error_code, error_string) 
    sys.exit (0) 

Die eindeutige Zeichenfolge postconnect_gateway_notify_lockkann geändert werden, um mehrere Programme zuzulassen, für die eine einzelne Instanz erzwungen werden muss.

Roberto Rosario
quelle
1
Roberto, sind Sie sicher, dass nach einer Kernel-Panik oder einem Hard-Reset die Datei \ 0postconnect_gateway_notify_lock beim Booten nicht vorhanden ist? In meinem Fall ist die AF_UNIX-Socket-Datei danach noch vorhanden, und dies zerstört die gesamte Idee. Die obige Lösung mit dem Erhalt einer Sperre für einen bestimmten Dateinamen ist in diesem Fall sehr zuverlässig.
Daniel Gurianov
2
Wie oben erwähnt, funktioniert diese Lösung unter Linux, jedoch nicht unter Mac OS X.
Bilal und Olga
2
Diese Lösung funktioniert nicht. Ich habe es unter Ubuntu 14.04 versucht. Führen Sie dasselbe Skript gleichzeitig in 2 Terminalfenstern aus. Sie laufen beide gut.
Dimon
1
Dies funktionierte für mich in Ubuntu 16. Und das Beenden des Prozesses auf irgendeine Weise ermöglichte es einem anderen, zu beginnen. Dimon Ich glaube, du hast in deinem Test etwas falsch gemacht. (Vielleicht haben Sie vergessen, Ihr Skript nach dem Ausführen des obigen Codes in den Ruhezustand zu versetzen, sodass es sofort beendet und der Socket freigegeben wurde.)
Luke
1
Es geht nicht um Schlaf. Der Code funktioniert aber nur als Inline-Code. Ich habe es in eine Funktion gebracht. Die Steckdose verschwand, sobald die Funktion vorhanden war.
Steve Cohen
25

Ich weiß nicht, ob es pythonisch genug ist, aber in der Java-Welt ist das Abhören eines definierten Ports eine ziemlich weit verbreitete Lösung, da es auf allen wichtigen Plattformen funktioniert und keine Probleme mit abstürzenden Programmen hat.

Ein weiterer Vorteil des Abhörens eines Ports besteht darin, dass Sie einen Befehl an die laufende Instanz senden können. Wenn die Benutzer das Programm beispielsweise ein zweites Mal starten, können Sie der laufenden Instanz einen Befehl senden, um sie anzuweisen, ein anderes Fenster zu öffnen (dies ist beispielsweise bei Firefox der Fall. Ich weiß nicht, ob sie TCP-Ports oder Named Pipes verwenden oder so etwas aber ').

Joachim Sauer
quelle
+1 dazu,
zumal
1
Verwenden Sie z import socket; s = socket.socket(socket.AF_INET, socket.SOCK_STREAM); s.bind(('localhost', DEFINED_PORT)). Ein OSErrorwird ausgelöst, wenn ein anderer Prozess an denselben Port gebunden ist.
Crishoj
12

Noch nie Python geschrieben, aber das habe ich gerade in mycheckpoint implementiert, um zu verhindern, dass es zweimal oder öfter von crond gestartet wird:

import os
import sys
import fcntl
fh=0
def run_once():
    global fh
    fh=open(os.path.realpath(__file__),'r')
    try:
        fcntl.flock(fh,fcntl.LOCK_EX|fcntl.LOCK_NB)
    except:
        os._exit(0)

run_once()

Ich habe den Vorschlag von Slava-N gefunden, nachdem ich ihn in einer anderen Ausgabe veröffentlicht habe (http://stackoverflow.com/questions/2959474). Diese Funktion wird als Funktion aufgerufen, sperrt die ausführende Skriptdatei (keine PID-Datei) und behält die Sperre bei, bis das Skript endet (normal oder fehlerhaft).

MD Klapwijk
quelle
Sehr elegant. Ich habe es geändert, damit es den Pfad aus den Argumenten des Skripts erhält.
Empfiehlt
10

Verwenden Sie eine PID-Datei. Sie haben einen bekannten Speicherort, "/ path / to / pidfile", und beim Start machen Sie so etwas (teilweise Pseudocode, weil ich vor dem Kaffee bin und nicht so hart arbeiten möchte):

import os, os.path
pidfilePath = """/path/to/pidfile"""
if os.path.exists(pidfilePath):
   pidfile = open(pidfilePath,"r")
   pidString = pidfile.read()
   if <pidString is equal to os.getpid()>:
      # something is real weird
      Sys.exit(BADCODE)
   else:
      <use ps or pidof to see if the process with pid pidString is still running>
      if  <process with pid == 'pidString' is still running>:
          Sys.exit(ALREADAYRUNNING)
      else:
          # the previous server must have crashed
          <log server had crashed>
          <reopen pidfilePath for writing>
          pidfile.write(os.getpid())
else:
    <open pidfilePath for writing>
    pidfile.write(os.getpid())

Mit anderen Worten, Sie überprüfen, ob eine PID-Datei vorhanden ist. Wenn nicht, schreiben Sie Ihre PID in diese Datei. Wenn die PID-Datei vorhanden ist, überprüfen Sie, ob die PID die PID eines laufenden Prozesses ist. Wenn ja, wird ein weiterer Live-Prozess ausgeführt. Fahren Sie einfach herunter. Wenn nicht, ist der vorherige Prozess abgestürzt. Protokollieren Sie ihn und schreiben Sie anstelle der alten Ihre eigene PID in die Datei. Dann fahre fort.

Charlie Martin
quelle
4
Dies hat eine Rennbedingung. Die Test-dann-Schreib-Sequenz kann eine Ausnahme von zwei Programmen auslösen, die fast gleichzeitig starten, keine Datei finden und versuchen, gleichzeitig zum Schreiben zu öffnen. Es sollte eine Ausnahme für eine auslösen, damit die andere fortfahren kann.
S.Lott
6

Sie haben bereits in einem anderen Thread eine Antwort auf eine ähnliche Frage gefunden. Der Vollständigkeit halber lesen Sie, wie Sie dies unter Windows mit dem Namen mutex erreichen können.

http://code.activestate.com/recipes/474070/

zgoda
quelle
5

Dies kann funktionieren.

  1. Versuchen Sie, eine PID-Datei an einem bekannten Speicherort zu erstellen. Wenn Sie versagen, hat jemand die Datei gesperrt, Sie sind fertig.

  2. Wenn Sie normal fertig sind, schließen und entfernen Sie die PID-Datei, damit sie von einer anderen Person überschrieben werden kann.

Sie können Ihr Programm in ein Shell-Skript einbinden, das die PID-Datei auch dann entfernt, wenn Ihr Programm abstürzt.

Sie können das Programm auch mit der PID-Datei beenden, wenn es hängt.

S.Lott
quelle
3

Die Verwendung einer Sperrdatei ist unter Unix ein weit verbreiteter Ansatz. Wenn es abstürzt, müssen Sie manuell bereinigen. Sie können die PID in der Datei speichern und beim Start prüfen, ob ein Prozess mit dieser PID vorliegt. Andernfalls können Sie die Sperrdatei überschreiben. (Sie benötigen jedoch auch eine Sperre für die Datei "read-file-check-pid-rewrite"). Was Sie brauchen, um pid zu bekommen und zu überprüfen, finden Sie im os- Paket. Die übliche Methode, um zu überprüfen, ob ein Prozess mit einer bestimmten PID vorliegt, besteht darin, ihm ein nicht schwerwiegendes Signal zu senden.

Andere Alternativen könnten darin bestehen, dies mit Herden- oder Posix-Semaphoren zu kombinieren.

Das Öffnen eines Netzwerk-Sockets, wie von saua vorgeschlagen, wäre wahrscheinlich das einfachste und portabelste.

Rolf Rander
quelle
3

Für jeden, der wxPython für seine Anwendung verwendet, können Sie die Funktion verwenden wx.SingleInstanceChecker hier dokumentierte .

Ich persönlich benutze eine Unterklasse von der wx.Appdie Verwendung von macht wx.SingleInstanceCheckerund kehrt Falseaus , OnInit()wenn es eine vorhandene Instanz von der App bereits Ausführung wie folgt:

import wx

class SingleApp(wx.App):
    """
    class that extends wx.App and only permits a single running instance.
    """

    def OnInit(self):
        """
        wx.App init function that returns False if the app is already running.
        """
        self.name = "SingleApp-%s".format(wx.GetUserId())
        self.instance = wx.SingleInstanceChecker(self.name)
        if self.instance.IsAnotherRunning():
            wx.MessageBox(
                "An instance of the application is already running", 
                "Error", 
                 wx.OK | wx.ICON_WARNING
            )
            return False
        return True

Dies ist ein einfacher Drop-In-Ersatz wx.App, der mehrere Instanzen verbietet. Um es zu nutzen einfach ersetzen wx.Appmit SingleAppwie so im Code:

app = SingleApp(redirect=False)
frame = wx.Frame(None, wx.ID_ANY, "Hello World")
frame.Show(True)
app.MainLoop()
Matt Coubrough
quelle
Nachdem ich einen Socket-Listing-Thread für einen Singleton codiert hatte, fand ich diesen, der großartig funktioniert und ich habe ihn bereits in ein paar Programmen installiert. Ich möchte jedoch das zusätzliche "Wakeup", das ich dem Singleton geben kann, damit ich ihn zum bringen kann vorne und in der Mitte eines großen Haufens überlappender Fenster. Außerdem: Der Link "hier dokumentiert" verweist auf eine ziemlich nutzlose automatisch generierte Dokumentation. Dies ist ein besserer Link
RufusVS
@ RufusVS Sie haben Recht - das ist ein viel besserer Dokumentationslink, haben die Antwort aktualisiert.
Matt Coubrough
3

Hier ist meine mögliche Nur-Windows-Lösung. Fügen Sie Folgendes in ein Modul ein, das möglicherweise "onlyone.py" heißt, oder was auch immer. Fügen Sie dieses Modul direkt in Ihre __ main __ Python-Skriptdatei ein.

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)

Erläuterung

Der Code versucht, einen Mutex mit einem Namen zu erstellen, der vom vollständigen Pfad zum Skript abgeleitet ist. Wir verwenden Schrägstriche, um mögliche Verwechslungen mit dem realen Dateisystem zu vermeiden.

Vorteile

  • Es sind keine Konfigurations- oder 'magischen' Bezeichner erforderlich. Verwenden Sie diese in so vielen verschiedenen Skripten wie nötig.
  • Keine veralteten Dateien mehr, der Mutex stirbt mit Ihnen.
  • Druckt beim Warten eine hilfreiche Nachricht
Keeely
quelle
3

Die beste Lösung hierfür unter Windows ist die Verwendung von Mutexen, wie von @zgoda vorgeschlagen.

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")

Einige Antworten verwenden fctnl(auch im @ sorin tendo-Paket enthalten), das unter Windows nicht verfügbar ist, und sollten Sie versuchen, Ihre Python-App mit einem Paket wie einzufrierenpyinstaller das statische Importe ausführt, wird ein Fehler ausgegeben.

Bei Verwendung der Sperrdateimethode tritt außerdem ein read-onlyProblem mit Datenbankdateien auf (dies ist aufgetreten sqlite3).

Chuck G.
quelle
2

Ich poste dies als Antwort, da ich ein neuer Benutzer bin und Stack Overflow mich noch nicht abstimmen lässt.

Die Lösung von Sorin Sbarnea funktioniert für mich unter OS X, Linux und Windows, und ich bin dafür dankbar.

Tempfile.gettempdir () verhält sich jedoch unter OS X und Windows auf die eine und unter anderen unter einigen / vielen / allen (?) * Nixen (ohne Berücksichtigung der Tatsache, dass OS X auch Unix ist!). Der Unterschied ist für diesen Code wichtig.

OS X und Windows verfügen über benutzerspezifische temporäre Verzeichnisse, sodass eine von einem Benutzer erstellte temporäre Datei für einen anderen Benutzer nicht sichtbar ist. Im Gegensatz dazu ist unter vielen Versionen von * nix (ich habe Ubuntu 9, RHEL 5, OpenSolaris 2008 und FreeBSD 8 getestet) das temporäre Verzeichnis / tmp für alle Benutzer.

Das heißt, wenn die Sperrdatei auf einem Mehrbenutzercomputer erstellt wird, wird sie in / tmp erstellt, und nur der Benutzer, der die Sperrdatei zum ersten Mal erstellt, kann die Anwendung ausführen.

Eine mögliche Lösung besteht darin, den aktuellen Benutzernamen in den Namen der Sperrdatei einzubetten.

Es ist erwähnenswert, dass sich die OP-Lösung zum Abrufen eines Ports auch auf einem Mehrbenutzercomputer schlecht verhält.

Philip Semanchuk
quelle
Für einige Leser (z. B. mich) ist das gewünschte Verhalten, dass nur eine Kopie ausgeführt werden kann, unabhängig davon, wie viele Benutzer beteiligt sind. Daher sind die tmp-Verzeichnisse pro Benutzer fehlerhaft, während die gemeinsam genutzten / tmp- oder Port-Sperren das gewünschte Verhalten aufweisen.
Jonathan Hartley
2

Ich benutze single_processauf meinem Gentoo;

pip install single_process

Beispiel :

from single_process import single_process

@single_process
def main():
    print 1

if __name__ == "__main__":
    main()   

Siehe: https://pypi.python.org/pypi/single_process/1.0

gkiwi
quelle
Schlägt in Py3 fehl. Das Paket scheint falsch konstruiert zu sein.
Ekevoo
Unter Windows bekomme ich: ImportError: Kein Modul namens fcntl
Andrew W. Phillips
1

Ich vermute immer wieder, dass es eine gute POSIXy-Lösung mit Prozessgruppen geben sollte, ohne das Dateisystem treffen zu müssen, aber ich kann es nicht ganz genau sagen. Etwas wie:

Beim Start sendet Ihr Prozess ein 'kill -0' an alle Prozesse in einer bestimmten Gruppe. Wenn solche Prozesse vorhanden sind, wird sie beendet. Dann tritt es der Gruppe bei. Keine anderen Prozesse verwenden diese Gruppe.

Dies hat jedoch eine Rennbedingung - mehrere Prozesse können dies genau zur gleichen Zeit tun und alle schließen sich der Gruppe an und laufen gleichzeitig. Wenn Sie eine Art Mutex hinzugefügt haben, um die Wasserdichtigkeit zu gewährleisten, benötigen Sie die Prozessgruppen nicht mehr.

Dies mag akzeptabel sein, wenn Ihr Prozess nur einmal pro Minute oder jede Stunde von cron gestartet wird, aber es macht mich ein bisschen nervös, dass es genau an dem Tag schief gehen würde, an dem Sie es nicht wollen.

Ich denke, das ist doch keine sehr gute Lösung, es sei denn, jemand kann sie verbessern?

Jonathan Hartley
quelle
1

Ich bin letzte Woche genau auf dieses Problem gestoßen, und obwohl ich einige gute Lösungen gefunden habe, habe ich beschlossen, ein sehr einfaches und sauberes Python-Paket zu erstellen und es auf PyPI hochzuladen. Es unterscheidet sich von tendo dadurch, dass es jeden String-Ressourcennamen sperren kann. Obwohl Sie sicherlich sperren könnten __file__, um den gleichen Effekt zu erzielen.

Installieren mit: pip install quicklock

Die Verwendung ist sehr einfach:

[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'!

Schauen Sie sich das an: https://pypi.python.org/pypi/quicklock

Nate Ferrero
quelle
1
Ich habe es gerade über "pip install quicklock" installiert, aber wenn ich versuche, es über "from quicklock import singleton" zu verwenden, erhalte ich eine Ausnahme: "ImportError: Name 'singleton' kann nicht importiert werden". Dies ist auf einem Mac.
Grayaii
Es stellt sich heraus, dass Quicklock mit Python 3 nicht funktioniert. Das ist der Grund, warum es für mich fehlgeschlagen ist.
Grayaii
Ja, sorry, es war überhaupt nicht zukunftssicher. Ich werde einen Beitrag begrüßen, damit es funktioniert!
Nate Ferrero
1

Aufbauend auf der Antwort von Roberto Rosario habe ich folgende Funktion:

SOCKET = None
def run_single_instance(uniq_name):
    try:
        import socket
        global SOCKET
        SOCKET = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
        ## Create an abstract socket, by prefixing it with null.
        # this relies on a feature only in linux, when current process quits, the
        # socket will be deleted.
        SOCKET.bind('\0' + uniq_name)
        return True
    except socket.error as e:
        return False

Wir müssen global SOCKETvaiable definieren, da es nur dann Müll wird, wenn der gesamte Prozess beendet wird. Wenn wir eine lokale Variable in der Funktion deklarieren, wird sie nach dem Beenden der Funktion nicht mehr gültig sein, sodass der Socket gelöscht wird.

Der gesamte Kredit sollte an Roberto Rosario gehen, da ich nur seinen Code klarstelle und ausarbeite. Und dieser Code funktioniert nur unter Linux, wie der folgende zitierte Text von https://troydhanson.github.io/network/Unix_domain_sockets.html erklärt:

Linux hat eine Besonderheit: Wenn der Pfadname für einen UNIX-Domänensocket mit einem Nullbyte \ 0 beginnt, wird sein Name nicht dem Dateisystem zugeordnet. Somit kollidiert es nicht mit anderen Namen im Dateisystem. Wenn ein Server seinen UNIX-Domain-Listening-Socket im abstrakten Namespace schließt, wird seine Datei gelöscht. Bei normalen UNIX-Domänensockets bleibt die Datei bestehen, nachdem der Server sie geschlossen hat.

makiko_fly
quelle
0

Linux Beispiel

Diese Methode basiert auf der Erstellung einer temporären Datei, die nach dem Schließen der Anwendung automatisch gelöscht wird. Beim Programmstart überprüfen wir das Vorhandensein der Datei. Wenn die Datei vorhanden ist (eine Ausführung steht noch aus), wird das Programm geschlossen. Andernfalls wird die Datei erstellt und die Ausführung des Programms fortgesetzt.

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
Kerwal
quelle
1
Willkommen bei Stack Overflow! Obwohl diese Antwort richtig sein mag, fügen Sie bitte eine Erklärung hinzu. Die Vermittlung der zugrunde liegenden Logik ist wichtiger als nur die Angabe des Codes, da dies dem OP und anderen Lesern hilft, dieses und ähnliche Probleme selbst zu beheben.
CodeMouse92
Ist das threadsicher? Scheint, als ob die Überprüfung und die
temporäre Dateierstellung
0

Auf einem Linux-System könnte man auch pgrep -anach der Anzahl der Instanzen fragen , das Skript befindet sich in der Prozessliste (Option -a zeigt die vollständige Befehlszeilenzeichenfolge an). Z.B

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__)))

Entfernen Sie, -u $UIDwenn die Einschränkung für alle Benutzer gelten soll. Haftungsausschluss: a) Es wird davon ausgegangen, dass der (Basis-) Name des Skripts eindeutig ist. B) Es können Rennbedingungen vorliegen.

user71769
quelle
-1
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()  
Ertan Özer
quelle
2
Willkommen bei Stack Overflow! Während dieser Codeblock die Frage beantworten kann, ist es am besten, wenn Sie eine kleine Erklärung dafür geben könnten, warum dies so ist. Bitte bearbeiten Sie Ihre Antwort, um eine solche Beschreibung aufzunehmen.
Artjom B.