Existe uma maneira Pythonic de ter apenas uma instância de um programa em execução?
A única solução razoável que eu encontrei é tentar executá-lo como um servidor em alguma porta e, em seguida, o segundo programa tentar ligar à mesma porta - falha. Mas não é realmente uma ótima idéia, talvez haja algo mais leve que isso?
(Leve em consideração que, às vezes, espera-se que o programa falhe, ou seja, segfault - para que coisas como "bloquear arquivo" não funcionem)
python
process
locking
mutual-exclusion
Slava V
fonte
fonte
Respostas:
O código a seguir deve fazer o trabalho, é multiplataforma e é executado no Python 2.4-3.2. Eu testei no Windows, OS X e Linux.
A versão mais recente do código está disponível singleton.py . Por favor, registre erros aqui .
Você pode instalar tend usando um dos seguintes métodos:
easy_install tendo
pip install tendo
fonte
Solução simples,
multiplataforma, encontrada em outra pergunta pelo zgoda :Muito parecido com a sugestão de S.Lott, mas com o código.
fonte
fcntl
módulo no Windows (embora a funcionalidade possa ser emulada).fg
. Portanto, parece que está funcionando corretamente para você (ou seja, o aplicativo ainda está ativo, mas suspenso, para que o bloqueio permaneça no lugar).lock_file_pointer = os.open(lock_path, os.O_WRONLY | os.O_CREAT)
Este código é específico do Linux. Ele usa soquetes de domínio UNIX 'abstratos', mas é simples e não deixa arquivos de bloqueio antigos. Eu prefiro a solução acima porque não requer uma porta TCP especialmente reservada.
A cadeia exclusiva
postconnect_gateway_notify_lock
pode ser alterada para permitir vários programas que precisam de uma única instância imposta.fonte
Não sei se é python o suficiente, mas no mundo Java, ouvir em uma porta definida é uma solução bastante usada, pois funciona em todas as principais plataformas e não tem problemas com programas que causam pane.
Outra vantagem de ouvir uma porta é que você pode enviar um comando para a instância em execução. Por exemplo, quando os usuários iniciam o programa pela segunda vez, você pode enviar à instância em execução um comando para dizer a ele para abrir outra janela (é o que o Firefox faz, por exemplo. Não sei se eles usam portas TCP ou pipes nomeados ou algo assim ').
fonte
import socket; s = socket.socket(socket.AF_INET, socket.SOCK_STREAM); s.bind(('localhost', DEFINED_PORT))
. UmOSError
será gerado se outro processo estiver vinculado à mesma porta.Nunca escrevi python antes, mas é isso que acabei de implementar no mycheckpoint, para impedir que ele seja iniciado duas ou mais vezes por crond:
Encontrei a sugestão do Slava-N depois de postar isso em outra edição (http://stackoverflow.com/questions/2959474). Este é chamado como uma função, bloqueia o arquivo de scripts em execução (não um arquivo pid) e mantém o bloqueio até o final do script (normal ou erro).
fonte
Use um arquivo pid. Você tem um local conhecido, "/ path / to / pidfile" e, na inicialização, faz algo assim (parcialmente pseudocódigo porque eu sou pré-café e não quero trabalhar tanto):
Portanto, em outras palavras, você está verificando se existe um arquivo pid; caso contrário, escreva seu pid nesse arquivo. Se o arquivo pid existir, verifique se o pid é o pid de um processo em execução; Nesse caso, você tem outro processo ativo em execução, então desligue-o. Caso contrário, o processo anterior falhou, registre-o e, em seguida, escreva seu próprio pid no arquivo no lugar do antigo. Então continue.
fonte
Você já encontrou a resposta para uma pergunta semelhante em outro segmento, portanto, por uma questão de integridade, veja como obter o mesmo no Windows, chamado mutex.
http://code.activestate.com/recipes/474070/
fonte
Isso pode funcionar.
Tente criar um arquivo PID para um local conhecido. Se você falhar, alguém tiver o arquivo bloqueado, está pronto.
Quando terminar normalmente, feche e remova o arquivo PID, para que outra pessoa possa substituí-lo.
Você pode agrupar seu programa em um script de shell que remove o arquivo PID, mesmo se o seu programa travar.
Você também pode usar o arquivo PID para matar o programa se ele travar.
fonte
Usar um arquivo de bloqueio é uma abordagem bastante comum no unix. Se travar, você precisará limpar manualmente. Você pode armazenar o PID no arquivo e, na inicialização, verificar se existe um processo com esse PID, substituindo o arquivo de bloqueio, se não houver. (No entanto, você também precisa de um bloqueio em torno do arquivo read-file-check-pid-rewrite-file). Você encontrará o que precisa para obter e verificar o pid no sistema operacional pacote . A maneira comum de verificar se existe um processo com um determinado pid é enviar um sinal não fatal.
Outras alternativas poderiam ser combinadas com semáforos flock ou posix.
Abrir um soquete de rede, como a saua propôs, provavelmente seria o mais fácil e mais portátil.
fonte
Para quem usa o wxPython para seu aplicativo, você pode usar a função
wx.SingleInstanceChecker
documentada aqui .Eu pessoalmente uso uma subclasse de
wx.App
que faz uso dewx.SingleInstanceChecker
e retornosFalse
deOnInit()
se existe uma instância existente do aplicativo já execução assim:Este é um substituto simples
wx.App
que proíbe várias instâncias. Para usá-lo, basta substituirwx.App
porSingleApp
no seu código da seguinte forma:fonte
Aqui está minha eventual solução apenas para Windows. Coloque o seguinte em um módulo, talvez chamado de 'onlyone.py' ou qualquer outra coisa. Inclua esse módulo diretamente no seu arquivo de script python __ main __.
Explicação
O código tenta criar um mutex com o nome derivado do caminho completo para o script. Usamos barras para evitar possíveis confusões com o sistema de arquivos real.
Vantagens
fonte
A melhor solução para isso no Windows é usar mutexes, conforme sugerido por @zgoda.
Algumas respostas usam
fctnl
(incluído também no pacote @sorin tendo) que não está disponível no Windows e, se você tentar congelar seu aplicativo python usando um pacote como opyinstaller
que faz importações estáticas, gera um erro.Além disso, o uso do método de bloqueio de arquivo cria um
read-only
problema com os arquivos de banco de dados (com experiência nissosqlite3
).fonte
Estou postando isso como resposta porque sou um novo usuário e o Stack Overflow não me permite votar ainda.
A solução de Sorin Sbarnea funciona para mim no OS X, Linux e Windows, e sou grato por isso.
No entanto, tempfile.gettempdir () se comporta de uma maneira no OS X e Windows e outro sob outros / muitos / todos (?) * Nixes (ignorando o fato de o OS X também ser o Unix!). A diferença é importante para esse código.
O OS X e o Windows têm diretórios temporários específicos do usuário, portanto, um arquivo temporário criado por um usuário não fica visível para outro usuário. Por outro lado, em muitas versões do * nix (testei o Ubuntu 9, RHEL 5, OpenSolaris 2008 e FreeBSD 8), o diretório dir é / tmp para todos os usuários.
Isso significa que, quando o arquivo de bloqueio é criado em uma máquina multiusuário, ele é criado em / tmp e somente o usuário que cria o arquivo de bloqueio pela primeira vez poderá executar o aplicativo.
Uma solução possível é incorporar o nome de usuário atual no nome do arquivo de bloqueio.
Vale ressaltar que a solução do OP de pegar uma porta também se comportará mal em uma máquina multiusuário.
fonte
Eu uso
single_process
no meu gentoo;exemplo :
consulte: https://pypi.python.org/pypi/single_process/1.0
fonte
Eu continuo suspeitando que deveria haver uma boa solução POSIXy usando grupos de processos, sem ter que acessar o sistema de arquivos, mas não consigo identificá-lo. Algo como:
Na inicialização, seu processo envia um 'kill -0' para todos os processos em um grupo específico. Se existir algum desses processos, ele será encerrado. Então ele se junta ao grupo. Nenhum outro processo usa esse grupo.
No entanto, isso tem uma condição de corrida - vários processos podem fazer isso exatamente ao mesmo tempo e acabam ingressando no grupo e funcionando simultaneamente. No momento em que você adicionou algum tipo de mutex para torná-lo à prova d'água, você não precisa mais dos grupos de processos.
Isso pode ser aceitável se o seu processo for iniciado apenas pelo cron, uma vez a cada minuto ou a cada hora, mas me deixa um pouco nervoso que isso daria errado exatamente no dia em que você não deseja.
Acho que essa não é uma solução muito boa, a menos que alguém possa melhorar isso?
fonte
Encontrei exatamente esse problema na semana passada e, embora tenha encontrado boas soluções, decidi criar um pacote python muito simples e limpo e o enviei para o PyPI. Difere de ter, pois pode bloquear qualquer nome de recurso de cadeia. Embora você certamente possa bloquear
__file__
para obter o mesmo efeito.Instale com:
pip install quicklock
Usá-lo é extremamente simples:
Dê uma olhada: https://pypi.python.org/pypi/quicklock
fonte
Com base na resposta de Roberto Rosario, proponho a seguinte função:
Precisamos definir a
SOCKET
disponibilidade global, pois ela só será coletada como lixo quando todo o processo for encerrado. Se declararmos uma variável local na função, ela ficará fora do escopo após a saída da função, portanto, o soquete será excluído.Todo o crédito deve ser para Roberto Rosario, já que apenas esclareço e elaborei seu código. E esse código funcionará apenas no Linux, como o seguinte texto citado em https://troydhanson.github.io/network/Unix_domain_sockets.html explica:
fonte
exemplo linux
Este método é baseado na criação de um arquivo temporário excluído automaticamente após o fechamento do aplicativo. no lançamento do programa, verificamos a existência do arquivo; se o arquivo existe (há uma execução pendente), o programa é fechado; caso contrário, ele cria o arquivo e continua a execução do programa.
fonte
Em um sistema Linux, também é possível solicitar
pgrep -a
o número de instâncias, o script é encontrado na lista de processos (a opção -a revela a cadeia de caracteres completa da linha de comandos). Por exemploRemova
-u $UID
se a restrição se aplicar a todos os usuários. Isenção de responsabilidade: a) presume-se que o nome do script (base) seja único; b) pode haver condições de corrida.fonte
fonte