问题
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