Python 确保只有一个程序实例正在运行
有没有一种Pythonic方法可以让一个程序只运行一个实例 我提出的唯一合理的解决方案是尝试在某个端口上作为服务器运行它,然后第二个程序尝试绑定到同一个端口——失败了。但这不是一个好主意,也许还有比这更轻的东西Python 确保只有一个程序实例正在运行,python,process,locking,mutual-exclusion,Python,Process,Locking,Mutual Exclusion,有没有一种Pythonic方法可以让一个程序只运行一个实例 我提出的唯一合理的解决方案是尝试在某个端口上作为服务器运行它,然后第二个程序尝试绑定到同一个端口——失败了。但这不是一个好主意,也许还有比这更轻的东西 (考虑到程序有时可能会失败,例如segfault-所以像“lock file”这样的东西不起作用)我不知道它是否足够pythonic,但在Java世界中,在定义的端口上侦听是一种非常广泛使用的解决方案,因为它可以在所有主要平台上工作,并且不会出现程序崩溃的问题 监听端口的另一个优点是可以
(考虑到程序有时可能会失败,例如segfault-所以像“lock file”这样的东西不起作用)我不知道它是否足够pythonic,但在Java世界中,在定义的端口上侦听是一种非常广泛使用的解决方案,因为它可以在所有主要平台上工作,并且不会出现程序崩溃的问题 监听端口的另一个优点是可以向正在运行的实例发送命令。例如,当用户第二次启动程序时,您可以向正在运行的实例发送一个命令,告诉它打开另一个窗口(例如,Firefox就是这样做的。我不知道他们是否使用TCP端口或命名管道或类似的东西)。这可能有效
如果程序挂起,您还可以使用PID文件终止程序。在unix上使用锁文件是一种非常常见的方法。如果它崩溃,你必须手动清理。您可以将PID存储在文件中,并在启动时检查是否有具有此PID的进程,如果没有,则覆盖锁定文件。(但是,您还需要在读取文件check pid rewrite文件周围设置一个锁)。您将在-包中找到获取和检查pid所需的内容。检查是否存在具有给定pid的进程的常用方法是向其发送非致命信号 其他替代方案可以是将其与flock或posix信号量相结合
按照saua的建议,打开网络套接字可能是最简单、最方便的方法。使用pid文件。您有一些已知的位置,“/path/to/pidfile”,在启动时,您会执行类似的操作(部分是伪代码,因为我还没喝咖啡,不想那么辛苦地工作):
导入操作系统,操作系统路径
pidfilePath=“”/path/to/pidfile“”
如果os.path.exists(pidfilePath):
pidfile=open(pidfilePath,“r”)
pidString=pidfile.read()
如果:
#有些事情真的很奇怪
系统出口(坏代码)
其他:
如果:
系统退出(ALREADAYRUNNING)
其他:
#以前的服务器一定崩溃了
写入(os.getpid())
其他:
写入(os.getpid())
换句话说,你要检查一个pidfile是否存在;如果没有,请将pid写入该文件。如果pid文件确实存在,则检查pid是否为正在运行的进程的pid;如果是这样,那么您已经有另一个活动进程在运行,所以只需关闭。如果没有,则前一个进程崩溃,所以记录它,然后将您自己的pid写入该文件以代替旧的pid。然后继续。简单的跨平台解决方案,可通过以下方式在中找到:
导入fcntl
导入操作系统
导入系统
def实例已在运行(label=“default”):
"""
检测带有标签的实例是否已在全局运行
在操作系统级别。
使用“os.open”可确保文件指针不会被关闭
在函数的作用域退出后由Python的垃圾收集器执行。
当程序退出时,锁将被释放,或者可能被释放
如果文件指针已关闭,则释放。
"""
lock_file_pointer=os.open(f“/tmp/instance_{label}.lock”,仅限os.O_)
尝试:
fcntl.lockf(lock_文件_指针,fcntl.lock_EX | fcntl.lock_NB)
已在运行=错误
除IOError外:
已在运行=真
返回已运行
与S.Lott的建议非常相似,但是使用代码。以下代码应该可以完成这项工作,它是跨平台的,在Python 2.4-3.2上运行。我在Windows、OSX和Linux上测试了它
from tendo import singleton
me = singleton.SingleInstance() # will sys.exit(-1) if other instance is running
最新的代码版本可用。求你了
您可以使用以下方法之一进行安装:
easy\u install tendo
pip安装tendo
- 通过从
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\u gateway\u notify\u lock
可以更改为允许多个程序需要执行单个实例。我将此作为答案发布,因为我是新用户,堆栈溢出还不允许我投票
Sorin Sbarnea的解决方案在OS X、Linux和Windows下对我有效,我对此表示感谢
但是,tempfile.gettempdir()在OS X和Windows下的行为是一种方式,在其他一些/many/all(?)*nixes下的行为是另一种方式(忽略OS X也是Unix的事实!)。这种差异对于这段代码很重要
OS X和Windows具有特定于用户的临时目录,因此一个用户创建的临时文件对另一个用户不可见。相比之下,在许多版本的*nix下(我测试了Ubuntu 9、RHEL 5、OpenSolaris 2008和FreeBSD 8),所有用户的临时目录都是/tmp
这意味着在多用户机器上创建锁文件时,它是在/tmp中创建的,只有第一次创建锁文件的用户才能运行应用程序
一种可能的解决方案是在锁文件的名称中嵌入当前用户名
值得注意的是,OP的抓取端口解决方案在多用户机器上也会出现错误行为。以前从未编写过python,但这是
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)
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()
pip install single_process
from single_process import single_process
@single_process
def main():
print 1
if __name__ == "__main__":
main()
[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'!
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
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
app = SingleApp(redirect=False)
frame = wx.Frame(None, wx.ID_ANY, "Hello World")
frame.Show(True)
app.MainLoop()
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)
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()
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__)))
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")
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
from win32event import CreateMutex
from win32api import CloseHandle, GetLastError
from winerror import ERROR_ALREADY_EXISTS
import sys
class singleinstance:
""" Limits application to single instance """
def __init__(self):
self.mutexname = "testmutex_{D0E858DF-985E-4907-B7FB-8D732C3FC3B9}"
self.mutex = CreateMutex(None, False, self.mutexname)
self.lasterror = GetLastError()
def alreadyrunning(self):
return (self.lasterror == ERROR_ALREADY_EXISTS)
def __del__(self):
if self.mutex:
CloseHandle(self.mutex)
# do this at beginnig of your application
myapp = singleinstance()
# check is another instance of same program running
if myapp.alreadyrunning():
print ("Another instance of this program is already running")
sys.exit(1)
import os
class SingleInstanceChecker:
def __init__(self, id):
if isWin():
ensure_win32api()
self.mutexname = id
self.lock = win32event.CreateMutex(None, False, self.mutexname)
self.running = (win32api.GetLastError() == winerror.ERROR_ALREADY_EXISTS)
else:
ensure_fcntl()
self.lock = open(f"/tmp/isnstance_{id}.lock", 'wb')
try:
fcntl.lockf(self.lock, fcntl.LOCK_EX | fcntl.LOCK_NB)
self.running = False
except IOError:
self.running = True
def already_running(self):
return self.running
def __del__(self):
if self.lock:
try:
if isWin():
win32api.CloseHandle(self.lock)
else:
os.close(self.lock)
except Exception as ex:
pass
# ---------------------------------------
# Utility Functions
# Dynamically load win32api on demand
# Install with: pip install pywin32
win32api=winerror=win32event=None
def ensure_win32api():
global win32api,winerror,win32event
if win32api is None:
import win32api
import winerror
import win32event
# Dynamically load fcntl on demand
# Install with: pip install fcntl
fcntl=None
def ensure_fcntl():
global fcntl
if fcntl is None:
import fcntl
def isWin():
return (os.name == 'nt')
# ---------------------------------------
import time, sys
def main(argv):
_timeout = 10
print("main() called. sleeping for %s seconds" % _timeout)
time.sleep(_timeout)
print("DONE")
if __name__ == '__main__':
SCR_NAME = "my_script"
sic = SingleInstanceChecker(SCR_NAME)
if sic.already_running():
print("An instance of {} is already running.".format(SCR_NAME))
sys.exit(1)
else:
main(sys.argv[1:])
import time
from contextlib import contextmanager
from django.core.cache import cache
@contextmanager
def memcache_lock(lock_key, lock_value, lock_expire):
timeout_at = time.monotonic() + lock_expire - 3
# cache.add fails if the key already exists
status = cache.add(lock_key, lock_value, lock_expire)
try:
yield status
finally:
# memcache delete is very slow, but we have to use it to take
# advantage of using add() for atomic locking
if time.monotonic() < timeout_at and status:
# don't release the lock if we exceeded the timeout
# to lessen the chance of releasing an expired lock owned by someone else
# also don't release the lock if we didn't acquire it
cache.delete(lock_key)
LOCK_EXPIRE = 60 * 10 # Lock expires in 10 minutes
def main():
lock_name, lock_value = "lock_1", "locked"
with memcache_lock(lock_name, lock_value, LOCK_EXPIRE) as acquired:
if acquired:
# single instance code here:
pass
if __name__ == "__main__":
main()
import os
from contextlib import contextmanager
from time import sleep
class ExceptionTaskInProgress(Exception):
pass
# Context manager for suppressing exceptions
class SuppressException:
def __init__(self):
pass
def __enter__(self):
return self
def __exit__(self, *exc):
return True
# Context manager for task
class TaskSingleInstance:
def __init__(self, task_name, lock_path):
self.task_name = task_name
self.lock_path = lock_path
self.lock_filename = os.path.join(self.lock_path, self.task_name + ".lock")
if os.path.exists(self.lock_filename):
raise ExceptionTaskInProgress("Resource already in use")
def __enter__(self):
self.fl = open(self.lock_filename, "w")
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.fl.close()
os.unlink(self.lock_filename)
# Here the task is silently interrupted
# if it is already running on another instance.
def main1():
task_name = "task1"
tmp_filename_path = "."
with SuppressException():
with TaskSingleInstance(task_name, tmp_filename_path):
print("The task `{}` has started.".format(task_name))
# The single task instance code is here.
sleep(5)
print("The task `{}` has completed.".format(task_name))
# Here the task is interrupted with a message
# if it is already running in another instance.
def main2():
task_name = "task1"
tmp_filename_path = "."
try:
with TaskSingleInstance(task_name, tmp_filename_path):
print("The task `{}` has started.".format(task_name))
# The single task instance code is here.
sleep(5)
print("Task `{}` completed.".format(task_name))
except ExceptionTaskInProgress as ex:
print("The task `{}` is already running.".format(task_name))
if __name__ == "__main__":
main1()
main2()