how can I disable multiple instances of a python script?

拜拜、爱过 提交于 2021-02-17 06:41:06

问题


I made a python program and I want to disable the option to launch it multiple times in parallel.

I searched the web and found that I need to do it with socket and ports, but I didn't find any example of how to do it(I know the steps, but I need to search every step on google and I am not so sure how to start).
I also found that there might be libraries that can do that but to specific operating systems. My script only targets Windows 10, so I need to find a library that does that to Windows if there is one.
Do you know such libraries or can you give a more detailed example of how to make this socket listener?
The results that I found on the web were actually just one question in python 2.7 (I am using 3.8): https://stackoverflow.com/a/24381532/13786031


回答1:


I'm not sure if it's possible to reliably detect if one instance (process) of the application is already running, but one possible way to do this is to reserve a TCP port for the program. The first application that starts will listen on that port and act like the server. Since only process can listen at the same port at the same time, all following processes will fail to create a listen socket and could then act like a client.

You could use a multiprocessing.connection to implement this. The following is a working example to demonstrate the concept:

import multiprocessing.connection
import sys

SERVER_HOST = 'localhost'
SERVER_PORT = 6000

g_total = 0


def process_numbers(numbers):
    global g_total
    for number in numbers:
        g_total += number
        print('Adding number %d. Total is now %d.' % (number, g_total))


def server_main(listener, numbers):
    process_numbers(numbers)
    print('Starting server.')
    while True:
        client = listener.accept()
        numbers = list(client.recv())
        client.close()
        if not numbers:
            break
        process_numbers(numbers)


def client_main(numbers):
    client = multiprocessing.connection.Client((SERVER_HOST, SERVER_PORT), 'AF_INET')
    print('Starting client.')
    client.send(numbers)


def main():
    numbers = map(int, sys.argv[1:])
    try:
        listener = multiprocessing.connection.Listener((SERVER_HOST, SERVER_PORT), 'AF_INET')
    except OSError:
        client_main(numbers)
    else:
        server_main(listener, numbers)

if __name__ == '__main__':
    main()

Assuming you have saved the above code as main.py, calling py main.py 1 2 3 will print the following output:

Adding number 1. Total is now 1.
Adding number 2. Total is now 3.
Adding number 3. Total is now 6.
Starting server.

The application will keep running. Now open a second terminal and run py main.py 4 5 6. The script will only print:

Starting client.

However, your first application (acting as the server) will now print:

Adding number 4. Total is now 10.
Adding number 5. Total is now 15.
Adding number 6. Total is now 21.

If you repeat the command it prints:

Adding number 4. Total is now 25.
Adding number 5. Total is now 30.
Adding number 6. Total is now 36.

and so on. You can quit the server by calling the client without arguments.




回答2:


If you bind yourself to a specific port, you will reserve this port for your instance of the program, and new instances will be blocked when trying to bind to this port.

import socket               # Import socket module

s = socket.socket()         # Create a socket object
host = socket.gethostname() # Get local machine name
port = 12345                # Reserve a port for your service.
s.bind((host, port))        # Bind to the port



回答3:


Check on how to get the current running processes in your code and check if your formal py application name is already listed in the current running processes obtained.

Check this sample for your reference: https://www.geeksforgeeks.org/python-get-list-of-running-processes/




回答4:


You could try something like this:

instance_manager.py:

import socket
import threading
import time

class OtherInstanceError(Exception):
    pass

class ProgramInstanceManager:
    _server_running = False
    _server_closed = False
    
    def __init__(self, port=35217, ip="127.0.0.1"):
        self.ip, self.port = ip, port
        is_first = self.check_instance()
        if not is_first:
            raise OtherInstanceError()

        self.setup_server()

    def check_instance(self):
        # setup a TCP socket with 2 sec timeout
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(2)
        try:
            sock.connect((self.ip, self.port))
        except socket.timeout:
            # no connection was made in time
            # so we can assume that we are the
            # only one
            return True
        sock.close()
        return False

    def setup_server(self):
        # setup a TCP server socket with 2 sec timeout
        self.serversock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.serversock.settimeout(2)
        self.serversock.bind((self.ip, self.port))

        self.server_thread = threading.Thread(target=self._server_run)
        self.server_thread.start()

    def _server_run(self):
        self._server_running = True
        self.serversock.listen(5)

        while self._server_running:
            try:
                conn, addr = self.serversock.accept()
                conn.close()
            except socket.timeout:
                pass

        self.serversock.close()
        self._server_closed = True

    def shutdown(self):
        self._server_running = False
        while not self._server_closed:
            time.sleep(0.1)

if __name__ == '__main__':
    p1 = ProgramInstanceManager()
    print("Started process 1")

    try:
        p2 = ProgramInstanceManager() # OtherInstanceError
        print("Started process 2")
    except OtherInstanceError:
        print("Failed to start process 2")

    # simulate closing the program
    p1.shutdown()
    print("Stopped process 1")

    p3 = ProgramInstanceManager()
    print("Started process 3")

To use in your program:

At the start:

from instance_manager import ProgramInstanceManager, OtherInstanceError
import sys

try:
    pim = ProgramInstanceManager()
except OtherInstanceError:
    print("Already got one instance running")
    sys.exit(1)

And at the end:

pim.shutdown()



回答5:


To kill all the processes with a filename in Linux, this is the command

kill -SIGINT `pgrep -f filename.py`


来源:https://stackoverflow.com/questions/63525619/how-can-i-disable-multiple-instances-of-a-python-script

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!