¿Existe una forma Pythonic de tener solo una instancia de un programa en ejecución?
La única solución razonable que se me ocurrió es intentar ejecutarlo como un servidor en algún puerto, luego el segundo programa que intenta vincularse al mismo puerto falla. Pero realmente no es una gran idea, ¿tal vez hay algo más liviano que esto?
(Tenga en cuenta que se espera que el programa falle a veces, es decir, segfault, por lo que cosas como "bloquear archivo" no funcionarán)
python
process
locking
mutual-exclusion
Slava V
fuente
fuente
Respuestas:
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.
La última versión del código está disponible singleton.py . Por favor, presente los errores aquí .
Puede instalar tender utilizando uno de los siguientes métodos:
easy_install tendo
pip install tendo
fuente
Solución simple
y multiplataforma, encontrada en otra pregunta de zgoda :Muy parecido a la sugerencia de S.Lott, pero con el código.
fuente
fcntl
módulo en Windows (aunque la funcionalidad podría emularse).fg
. Entonces, parece que está funcionando correctamente para usted (es decir, la aplicación aún está activa, pero suspendida, por lo que el bloqueo permanece en su lugar).lock_file_pointer = os.open(lock_path, os.O_WRONLY | os.O_CREAT)
Este código es específico de Linux. Utiliza sockets de dominio UNIX 'abstractos', pero es simple y no dejará archivos de bloqueo obsoletos. Lo prefiero a la solución anterior porque no requiere un puerto TCP especialmente reservado.
La cadena única
postconnect_gateway_notify_lock
se puede cambiar para permitir múltiples programas que necesitan que se aplique una sola instancia.fuente
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 que se bloquean.
Otra ventaja de escuchar un puerto es que puede enviar un comando a la instancia en ejecución. Por ejemplo, cuando los usuarios inician el programa por segunda vez, puede enviar a la instancia en ejecución un comando 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).
fuente
import socket; s = socket.socket(socket.AF_INET, socket.SOCK_STREAM); s.bind(('localhost', DEFINED_PORT))
. SeOSError
generará un si otro proceso está vinculado al mismo puerto.Nunca antes escribí python, pero esto es lo que acabo de implementar en mycheckpoint, para evitar que crond lo inicie dos veces o más:
Encontré la sugerencia de Slava-N después de publicar esto en otro número (http://stackoverflow.com/questions/2959474). Este se llama como una función, bloquea el archivo de scripts en ejecución (no un archivo pid) y mantiene el bloqueo hasta que el script finaliza (normal o error).
fuente
Utilice un archivo pid. Tienes una ubicación conocida, "/ ruta / a / pidfile" y al iniciar haces algo como esto (parcialmente pseudocódigo porque estoy pre-café y no quiero trabajar tan duro):
Entonces, en otras palabras, está comprobando si existe un archivo pid; si no, escriba su pid en ese archivo. Si el archivo pid existe, compruebe si es el pid de un proceso en ejecución; si es así, entonces tiene otro proceso en vivo ejecutándose, así que 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. Entonces continúe.
fuente
Ya encontró la respuesta a una pregunta similar en otro hilo, por lo que, para completar, vea cómo lograr lo mismo en Windows con el nombre mutex.
http://code.activestate.com/recipes/474070/
fuente
Esto puede funcionar.
Intente crear un archivo PID en una ubicación conocida. Si falla, alguien tiene el archivo bloqueado, ya está.
Cuando termine normalmente, cierre y elimine el archivo PID para que otra persona pueda sobrescribirlo.
Puede envolver su programa en un script de shell que elimina el archivo PID incluso si su programa falla.
También puede usar el archivo PID para matar el programa si se cuelga.
fuente
El uso de un archivo de bloqueo es un enfoque bastante común en Unix. Si falla, debe limpiar manualmente. Puede almacenar el PID en el archivo y, en el inicio, verificar si hay un proceso con este PID, anulando el archivo de bloqueo si no es así. (Sin embargo, también necesita un bloqueo alrededor de read-file-check-pid-rewrite-file). Encontrará lo que necesita para obtener y verificar pid en el paquete del sistema operativo. La forma común de comprobar si existe un proceso con un pid determinado es enviarle una señal no fatal.
Otras alternativas podrían combinar esto con semáforos flock o posix.
Abrir un conector de red, como propuso saua, probablemente sería lo más fácil y portátil.
fuente
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
la que hace uso dewx.SingleInstanceChecker
y vuelveFalse
aOnInit()
si hay una instancia existente de la aplicación ya la ejecución de este modo:Este es un simple reemplazo directo
wx.App
que prohíbe múltiples instancias. Para usarlo, simplemente reemplazarwx.App
conSingleApp
su código de este modo:fuente
Aquí está mi solución eventual solo para Windows. Ponga lo siguiente en un módulo, quizás llamado 'onlyone.py', o lo que sea. Incluya ese módulo directamente en su archivo de secuencia de comandos de Python __ principal __.
Explicación
El código intenta crear un mutex con un nombre derivado de la ruta completa al script. Usamos barras diagonales para evitar posibles confusiones con el sistema de archivos real.
Ventajas
fuente
La mejor solución para esto en Windows es usar mutex como lo sugiere @zgoda.
Algunas respuestas usan
fctnl
(incluido también en el paquete @sorin tendo) que no está disponible en Windows y si intenta congelar su aplicación de Python usando un paquete comopyinstaller
que hace importaciones estáticas, arroja un error.Además, el uso del método de archivo de bloqueo crea un
read-only
problema con los archivos de la base de datos (experimentado esto consqlite3
).fuente
Estoy publicando esto como respuesta porque soy un usuario nuevo y Stack Overflow todavía no me deja votar.
La solución de Sorin Sbarnea me funciona en OS X, Linux y Windows, y estoy agradecido por ello.
Sin embargo, tempfile.gettempdir () se comporta de una manera en OS X y Windows y de otra en 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, en 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 el archivo de bloqueo se crea en una máquina multiusuario, se crea en / tmp y solo el usuario que crea el archivo de bloqueo por primera vez podrá ejecutar la aplicación.
Una posible solución es incrustar el nombre de usuario actual en el nombre del archivo de bloqueo.
Vale la pena señalar que la solución del OP de tomar un puerto también se comportará mal en una máquina multiusuario.
fuente
Lo uso
single_process
en mi gentoo;ejemplo :
consulte: https://pypi.python.org/pypi/single_process/1.0
fuente
Sigo sospechando que debería haber una buena solución POSIXy usando grupos de procesos, sin tener que presionar el sistema de archivos, pero no puedo concretarlo. Algo como:
En el inicio, su proceso envía un 'kill -0' a todos los procesos en un grupo en particular. Si existe alguno de estos procesos, sale. 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 ejecutándose simultáneamente. Cuando haya agregado algún tipo de mutex para que sea hermético, ya no necesitará los grupos de procesos.
Esto podría ser aceptable si su proceso solo se inicia con cron, una vez cada minuto o cada hora, pero me pone un poco nervioso que salga mal precisamente el día en que no lo desea.
Supongo que esta no es una muy buena solución después de todo, a menos que alguien pueda mejorarla.
fuente
Me encontré con este problema exacto la semana pasada, y aunque encontré algunas buenas soluciones, decidí hacer un paquete de Python muy simple y limpio y lo cargué en PyPI. Se diferencia de tendo en que puede bloquear cualquier nombre de recurso de cadena. Aunque ciertamente podrías bloquear
__file__
para lograr el mismo efecto.Instalar con:
pip install quicklock
Usarlo es extremadamente simple:
Eche un vistazo: https://pypi.python.org/pypi/quicklock
fuente
Basándome en la respuesta de Roberto Rosario, se me ocurre la siguiente función:
Necesitamos definir global
SOCKET
variable ya que solo se recolectará basura cuando se cierre todo el proceso. Si declaramos una variable local en la función, saldrá del alcance después de que la función salga, por lo que se eliminará el socket.Todo el mérito debe ser para Roberto Rosario, ya que solo aclaro y amplío su código. Y este código solo funcionará en Linux, como explica el siguiente texto citado de https://troydhanson.github.io/network/Unix_domain_sockets.html :
fuente
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. al iniciar el programa verificamos la existencia del archivo; si el archivo existe (hay una ejecución pendiente), el programa se cierra; de lo contrario, crea el archivo y continúa la ejecución del programa.
fuente
En un sistema Linux, también se puede solicitar
pgrep -a
el número de instancias, el script se encuentra en la lista de procesos (la opción -a revela la cadena de línea de comandos completa). P.ejElimínelo
-u $UID
si la restricción debe aplicarse a todos los usuarios. Descargo de responsabilidad: a) se asume que el nombre del script (base) es único, b) puede haber condiciones de carrera.fuente
fuente