proyectos ejemplos python locking

python - ejemplos - django



Python: instancia Ășnica del programa (17)

Aquí está mi solución eventual solo para Windows. Coloque lo siguiente en un módulo, tal vez llamado ''onlyone.py'', o lo que sea. Incluya ese módulo directamente en su archivo de script __ principal __ 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)

Explicación

El código intenta crear un mutex con nombre derivado de la ruta completa al script. Utilizamos barras diagonales para evitar posibles confusiones con el sistema de archivos real.

Ventajas

  • No necesita configuración o identificadores ''mágicos'', utilícelos en tantos scripts diferentes como sea necesario.
  • No quedan archivos obsoletos, el mutex muere contigo.
  • Imprime un mensaje útil cuando espera

¿Hay una manera Pythonic de tener solo una instancia de un programa ejecutándose?

La única solución razonable que se me ocurrió es tratar de ejecutarlo como un servidor en algún puerto, luego el segundo programa intenta vincularse al mismo puerto: falla. Pero en realidad no es una gran idea, tal vez hay algo más ligero que esto?

(Tenga en cuenta que se espera que el programa falle a veces, es decir, falla de segmentación, por lo que cosas como "archivo de bloqueo" no funcionarán)

Actualización : las soluciones ofrecidas son mucho más complejas y menos dependientes que tener un puerto ocupado con un servidor inexistente, así que tendría que ir con eso.


El siguiente código debería hacer el trabajo, es multiplataforma y se ejecuta en Python 2.4-3.2. Lo probé en Windows, OS X y Linux.

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

La última versión de código está disponible singleton.py . Por favor presente errores aquí .

Puede instalar tienden utilizando uno de los siguientes métodos:


Este código es específico de Linux. Utiliza conectores de dominio UNIX ''abstractos'', pero es simple y no dejará archivos obsoletos. Lo prefiero a la solución anterior porque no requiere un puerto TCP especialmente reservado.

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)

La cadena única postconnect_gateway_notify_lock se puede cambiar para permitir que múltiples programas necesiten una única instancia aplicada.


Esto puede funcionar

  1. Intente crear un archivo PID en una ubicación conocida. Si fallas, alguien tiene el archivo bloqueado, ya terminaste.

  2. Cuando termine normalmente, cierre y elimine el archivo PID, para que alguien más pueda sobrescribirlo.

Puede envolver su programa en un script de shell que elimine el archivo PID incluso si su programa falla.

También puede usar el archivo PID para matar el programa si se cuelga.


Estoy publicando esto como respuesta porque soy un nuevo usuario y no me deja votar todavía.

La solución de Sorin Sbarnea funciona para mí bajo OS X, Linux y Windows, y estoy agradecido por ello.

Sin embargo, tempfile.gettempdir () se comporta de una manera bajo OS X y Windows y otra bajo algunos / muchos / todos (?) * Nixes (ignorando el hecho de que OS X también es Unix!). La diferencia es importante para este código.

OS X y Windows tienen directorios temporales específicos del usuario, por lo que un archivo temporal creado por un usuario no es visible para otro usuario. Por el contrario, bajo muchas versiones de * nix (probé Ubuntu 9, RHEL 5, OpenSolaris 2008 y FreeBSD 8), el directorio temporal es / tmp para todos los usuarios.

Eso significa que cuando se crea el archivo de bloqueo en una máquina multiusuario, se crea en / tmp y solo el usuario que crea el archivo de bloqueo la primera vez podrá ejecutar la aplicación.

Una posible solución es insertar el nombre de usuario actual en el nombre del archivo de bloqueo.

Vale la pena señalar que la solución de OP de agarrar un puerto también se comportará mal en una máquina multiusuario.


Haría algo como:

if commands.getstatusoutput("mkdir /tmp/test")[0]: print "Exiting" sys.exit(1)

Y en algún lugar al final de mi código, eliminaré el directorio. mkdir es atómico y funciona bastante bien aquí.


Me encontré con este problema exacto la semana pasada, y aunque encontré algunas buenas soluciones, decidí hacer un paquete python muy simple y limpio y lo cargué en PyPI. Difiere de tendo en que puede bloquear cualquier nombre de recurso de cadena. Aunque ciertamente __file__ bloquear __file__ para lograr el mismo efecto.

Instalar con: pip install quicklock

Usarlo es extremadamente simple:

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

Eche un vistazo: https://pypi.python.org/pypi/quicklock


No sé si es lo suficientemente pitónico, pero en el mundo de Java, escuchar en un puerto definido es una solución bastante utilizada, ya que funciona en todas las plataformas principales y no tiene ningún problema con los programas bloqueados.

Otra ventaja de escuchar un puerto es que puedes enviar un comando a la instancia en ejecución. Por ejemplo, cuando los usuarios inician el programa por segunda vez, puede enviar un comando a la instancia en ejecución para indicarle que abra otra ventana (eso es lo que hace Firefox, por ejemplo. No sé si usan puertos TCP o canalizaciones con nombre o algo así, ''aunque).


Nunca antes había escrito python, pero esto es lo que acabo de implementar en mycheckpoint, para evitar que sea iniciado dos o más veces por crond:

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

Encontré la sugerencia de Slava-N después de publicar esto en otro número (http://.com/questions/2959474). Éste se llama como una función, bloquea el archivo de scripts de ejecución (no un archivo pid) y mantiene el bloqueo hasta que finalice el script (normal o error).


Para cualquiera que use wxPython para su aplicación, puede usar la función wx.SingleInstanceChecker documentada aquí .

Yo personalmente uso una subclase de wx.App que hace uso de wx.SingleInstanceChecker y devuelve False de OnInit() si ya hay una instancia existente de la aplicación ejecutándose así:

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

Este es un simple reemplazo wx.App para wx.App que prohíbe varias instancias. Para usarlo, simplemente reemplace wx.App con SingleApp en su código, así:

app = SingleApp(redirect=False) frame = wx.Frame(None, wx.ID_ANY, "Hello World") frame.Show(True) app.MainLoop()


Sigo sospechando que debería haber una buena solución POSIXy utilizando grupos de procesos, sin tener que acceder al sistema de archivos, pero no puedo eliminarlo. Algo como:

Al inicio, su proceso envía un ''kill -0'' a todos los procesos en un grupo particular. Si cualquiera de estos procesos existe, se cierra. Luego se une al grupo. Ningún otro proceso usa ese grupo.

Sin embargo, esto tiene una condición de carrera: múltiples procesos podrían hacer esto exactamente al mismo tiempo y todos terminan uniéndose al grupo y corriendo simultáneamente. Para cuando haya agregado algún tipo de exclusión mutua para hacerlo estanco, ya no necesitará los grupos de procesos.

Esto podría ser aceptable si su proceso solo se inicia cron, una vez cada minuto o cada hora, pero me pone un poco nervioso que podría salir mal precisamente el día en que no lo desee.

Supongo que esta no es una solución muy buena después de todo, a menos que alguien pueda mejorarla.


Solución simple, multiplataforma , encontrada en otra pregunta por zgoda :

import fcntl, sys pid_file = ''program.pid'' fp = open(pid_file, ''w'') try: fcntl.lockf(fp, fcntl.LOCK_EX | fcntl.LOCK_NB) except IOError: # another instance is running sys.exit(0)

Muy parecido a la sugerencia de S.Lott, pero con el código.


Usar un archivo de bloqueo es un enfoque bastante común en Unix. Si se bloquea, debe limpiarlo manualmente. Puede almacenar el PID en el archivo, y al inicio comprobar si hay un proceso con este PID, anulando el archivo de bloqueo si no. (Sin embargo, también necesita un bloqueo alrededor del archivo read-file-check-pid-rewrite). Encontrará lo que necesita para obtener y consultar pid en el paquete de os . La forma común de verificar si existe un proceso con un pid dado, es enviarle una señal no fatal.

Otras alternativas podrían ser combinar esto con los semáforos flock o posix.

Abrir un enchufe de red, como propuso saua, probablemente sea el más fácil y portátil.


Use un archivo pid. Usted tiene una ubicación conocida, "/ ruta / a / archivo pid" y al inicio hace algo como esto (parcialmente seudocódigo porque estoy pre-café y no quiero trabajar tan duro):

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

Entonces, en otras palabras, está comprobando si existe un archivo pidfile; si no, escriba su pid en ese archivo. Si el archivo pidfile existe, entonces verifique si el pid es el pid de un proceso en ejecución; si es así, entonces tiene otro proceso en vivo ejecutándose, así que simplemente cierre. De lo contrario, el proceso anterior se bloqueó, así que regístrelo y luego escriba su propio pid en el archivo en lugar del anterior. Luego continúa.



Yo uso single_process en mi gentoo;

pip install single_process

ejemplo :

from single_process import single_process @single_process def main(): print 1 if __name__ == "__main__": main()

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


ejemplo de Linux

Este método se basa en la creación de un archivo temporal que se elimina automáticamente después de cerrar la aplicación. el lanzamiento del programa verificamos la existencia del archivo; si el archivo existe (hay una ejecución pendiente), el programa está cerrado; de lo contrario, crea el archivo y continúa la ejecución del programa.

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