Python 确保只有一个程序实例正在运行

Python 确保只有一个程序实例正在运行,python,process,locking,mutual-exclusion,Python,Process,Locking,Mutual Exclusion,有没有一种Pythonic方法可以让一个程序只运行一个实例 我提出的唯一合理的解决方案是尝试在某个端口上作为服务器运行它,然后第二个程序尝试绑定到同一个端口——失败了。但这不是一个好主意,也许还有比这更轻的东西 (考虑到程序有时可能会失败,例如segfault-所以像“lock file”这样的东西不起作用)我不知道它是否足够pythonic,但在Java世界中,在定义的端口上侦听是一种非常广泛使用的解决方案,因为它可以在所有主要平台上工作,并且不会出现程序崩溃的问题 监听端口的另一个优点是可以

有没有一种Pythonic方法可以让一个程序只运行一个实例

我提出的唯一合理的解决方案是尝试在某个端口上作为服务器运行它,然后第二个程序尝试绑定到同一个端口——失败了。但这不是一个好主意,也许还有比这更轻的东西


(考虑到程序有时可能会失败,例如segfault-所以像“lock file”这样的东西不起作用)

我不知道它是否足够pythonic,但在Java世界中,在定义的端口上侦听是一种非常广泛使用的解决方案,因为它可以在所有主要平台上工作,并且不会出现程序崩溃的问题

监听端口的另一个优点是可以向正在运行的实例发送命令。例如,当用户第二次启动程序时,您可以向正在运行的实例发送一个命令,告诉它打开另一个窗口(例如,Firefox就是这样做的。我不知道他们是否使用TCP端口或命名管道或类似的东西)。

这可能有效

  • 尝试将PID文件创建到已知位置。如果你失败了,有人锁定了文件,你就完了

  • 正常完成后,关闭并删除PID文件,以便其他人可以覆盖它

  • 您可以将程序包装在shell脚本中,即使程序崩溃,该脚本也会删除PID文件


    如果程序挂起,您还可以使用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
    • 通过从

    此代码是特定于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\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()