确保只有一个单一实例的运行程序
-
22-08-2019 - |
题
是否有一个功能已大大增强的方式来只有一个实例的运行的程序?
唯一合理的解决方案,我来试图运行它作为一个服务器上的一些港口,然后是第二节目试图结合到同一口-失败。但它不是一个真正的伟大的想法,也许还有更多的东西轻于这个?
(考虑到这一程序预计将会失败,有时,即出现段错误-这样的东西像"加锁的文件"不会的工作)
解决方案
以下代码应该做的工作,它的跨平台上运行的Python2.4-3.2.我测试了它在Windows,OS X和Linux。
from tendo import singleton
me = singleton.SingleInstance() # will sys.exit(-1) if other instance is running
最新代码版本是 singleton.py.请 文件中错误在这里.
你可以安装倾向于使用以下方法:
easy_install tendo
pip install tendo
- 手动把它从 http://pypi.python.org/pypi/tendo
其他提示
此代码是Linux特有。它采用“抽象” UNIX域套接字,但它很简单,不会留下周围陈旧的锁文件。我喜欢它上面,因为它不需要专门保留TCP端口的解决方案。
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)
在唯一的字符串postconnect_gateway_notify_lock
可以改变,以允许需要执行的单个实例多个程序。
我不知道它是否够Python的,但在Java世界侦听定义的端口上是一个非常广泛的解决方案,因为它适用于所有主要平台和没有与崩溃的程序的任何问题。
监听端口的另一个好处是,你可以命令发送到正在运行的实例。例如,当用户启动程序的第二次,您可以发送的运行实例的命令来告诉它打开另一个窗口(这是火狐做什么,例如,我不知道,如果他们使用的TCP端口或命名管道类似的东西,“虽然)。
从未写过蟒蛇,但是这是我刚刚在mycheckpoint实施,以防止它被通过的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()
实测值斯拉瓦-N的建议中的另一个问题(http://stackoverflow.com/questions/2959474)张贴这之后。这个人是作为函数调用,锁定执行脚本文件(不是pid文件),并且保持锁定,直到脚本结束(正常或错误)。
使用一个pid文件。你有一些已知的位置,“/路径/到/ pidfile进程文件”,并在启动时你做这样的事情(部分伪代码,因为我前咖啡,不想工作,所有的辛苦):
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())
因此,换句话说,你要检查是否存在pidfile进程文件;如果不是,写你的PID到该文件。如果pidfile进程文件确实存在,然后检查,看看是否pid是正在运行的进程的PID;如果是这样,那么你已经有了另一台活动进程在运行,所以才关机。如果不是,那么前面的进程崩溃,所以记录它,然后写自己的PID号的文件来取代旧的。然后继续。
您已经找到回答类似的问题在另一个线程,因此对于全面考虑看看如何实现相同的Windows uning命名的互斥体。
这个可能的工作。
尝试创建一个PID文件的一个已知的位置。如果你失败了,有人具有的文件锁住,你在做。
当你完成通常情况下,关闭并删除PID文件,因此其他人能够复盖它。
你可以用你的节目脚本中删除PID文件,甚至如果你的程序的崩溃。
你可以,也使用PID文件杀害该程序,如果它挂起。
使用一个锁定文件是UNIX上的相当常见的方法。如果崩溃,您必须手动清理。你可以STOR的PID的文件中,并在启动时检查是否有与此PID的过程,覆盖锁定文件,如果没有。 (不过,还需要围绕锁定只读文件检查-PID重写文件)。你会发现你需要在 OS -package获取和检查PID。检查,如果存在一种方法,使用一个给定的PID,所述的常用方法是向它发送一个非致命信号。
其他替代方案可以与群或POSIX信号被结合于此。
打开的网络套接字,提议SAUA,很可能是最简单和最便携的。
任何人使用 wxPython 他们的应用程序,可以利用的功能 wx.SingleInstanceChecker
记录在这里.
我个人使用的一个子类 wx.App
这使得使用 wx.SingleInstanceChecker
和返回 False
从 OnInit()
如果有一个现有实例中的应用程序已经执行,像这样:
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
这是一个简单替换为 wx.App
禁止多个实例。使用它只是代替 wx.App
与 SingleApp
在你的代码就像这样:
app = SingleApp(redirect=False)
frame = wx.Frame(None, wx.ID_ANY, "Hello World")
frame.Show(True)
app.MainLoop()
这是我的最终Windows-唯一的解决办法。把下列入一个模块,也许是所谓的'onlyone.py',或什么的。包括那个模块,直接进入你的语句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)
解释
代码试图创建一个互斥的名称源自全路径的脚本。我们使用的前向-斜线,以避免潜在的混淆实际文件系统。
优点
- 没有配置或"魔法"的标识符需要,使用它在许多不同的脚本需要。
- 没有陈旧的文件保留,互斥死你。
- 指纹一个有用的消息时在等待
因为我是一个新的用户和堆栈溢出不会让我投票呢我张贴这作为一个答案。
索林Sbarnea的解决方案对我的作品在OS X,Linux和Windows,我对此表示感谢。
然而,tempfile.gettempdir()的行为等下在OS X和Windows的一种方式,另外一些/许多/所有(?)* nixes(忽略一个事实,即OS X也是Unix的!)。所不同的是这个代码很重要。
OS X和Windows具有用户特定的临时目录,所以由一个用户创建的临时文件是不可见的给其他用户。与此相反,下* nix中的许多版本(I测试Ubuntu的9,RHEL 5,2008年的OpenSolaris和FreeBSD 8),临时dir是/对所有用户tmp中。
这意味着,一个多用户机器上创建锁文件时,它的在/ tmp和只有用户谁创建锁文件在第一时间将能够运行该应用程序创建的。
一个可能的解决方案是嵌入当前用户名在锁定的文件的名称。
值得注意的是抓住一个端口的OP的解决方案也将出现异常的多用户计算机上。
我用single_process
我巴布亚;
pip install single_process
示例强>:
from single_process import single_process
@single_process
def main():
print 1
if __name__ == "__main__":
main()
最好的解决方案上窗户是使用互斥的建议,通过@优.
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")
一些答复使用 fctnl
(也包括在@索林天童包裹的),这是不适用于windows和你应该试图冻结你的蟒蛇的应用程序使用包喜欢 pyinstaller
这并静态的进口,它将引发一个错误。
此外,使用锁定文件的方法,创建了一个 read-only
问题的数据库文件(经历这 sqlite3
).
我一直怀疑,就必须有使用进程组,而无需打文件系统的一个很好的解决方案POSIXy,但我不能很钉子下来。是这样的:
在启动时,你的过程在一个特定的组发送“杀-0”的所有过程。如果存在任何这样的过程,它退出。然后加入组。没有其他进程使用该组。
然而,这具有的竞争条件 - 多个进程可能所有为此在完全相同的时间和所有最终加入该组并且同时运行。到时候你已经添加了某种互斥体,使其不漏水,您不再需要处理组。
如果您的过程只会由cron启动,这可能是可以接受的,每分钟一次或每隔一小时,但它让我有点紧张,这会出问题正是在这一天,当你不希望它。
我想这是不是一个很好的解决方案毕竟,除非有人能改进它?
上周我就遇到了这个确切的问题,虽然我也找到了一些好的解决办法,我决定做一个非常简单和干净的Python包,并将其上传到PyPI中。它不同之处在于它可以锁定任何字符串资源名称肌腱。虽然可以肯定锁定__file__
来达到同样的效果。
用安装:pip install quicklock
使用它极其简单:
[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'!
Linux示例
此方法是基于创建关闭应用程序之后自动删除临时文件。 程序启动,我们验证该文件的存在; 如果该文件存在(存在未决的执行),则程序是封闭的;否则创建该文件,并继续程序的执行。
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
在Linux系统中的一个还可以问
pgrep -a
为实例的数量,脚本
在进程列表中找到(选项-a揭示
完整的命令行字符串)。 E.g。
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__)))
移除-u $UID
如果限制应适用于所有的用户。
免责声明:一)假设脚本的(基地)的名称是唯一的,二)可能存在竞争条件
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()