Я реализую класс Server
в CPython 3.7 в Windows 10 с методом Server.serve
, который запускает обслуживание навсегда, и методом Server.shutdown
, который останавливает обслуживание. Мне нужно запустить несколько экземпляров сервера в подпроцессах.
Запуск экземпляра сервера в подпотоке останавливает экземпляр, как и ожидалось:
import threading
import time
class Server:
def __init__(self):
self.shutdown_request = False
def serve(self):
print("serving")
while not self.shutdown_request:
print("hello")
time.sleep(1)
print("done")
def shutdown(self):
print("stopping")
self.shutdown_request = True
if __name__ == "__main__":
server = Server()
threading.Thread(target=server.serve).start()
time.sleep(5)
server.shutdown()
Однако запуск экземпляра сервера в подпроцессе неожиданно не останавливает экземпляр:
import multiprocessing
import time
class Server:
def __init__(self):
self.shutdown_request = False
def serve(self):
print("serving")
while not self.shutdown_request:
print("hello")
time.sleep(1)
print("done")
def shutdown(self):
print("stopping")
self.shutdown_request = True
if __name__ == "__main__":
server = Server()
multiprocessing.Process(target=server.serve).start()
time.sleep(5)
server.shutdown()
Я подозреваю, что в случае многопроцессорности атрибут self.shutdown_request
не используется совместно родительским процессом и подпроцессом, и поэтому вызов server.shutdown()
не влияет на работающий экземпляр сервера в подпроцессе.
Я знаю, что могу решить это с помощью multiprocessing.Event
:
import multiprocessing
import time
class Server:
def __init__(self, shutdown_event):
self.shutdown_event = shutdown_event
def serve(self):
print("serving")
while not self.shutdown_event.is_set():
print("hello")
time.sleep(1)
print("done")
if __name__ == "__main__":
shutdown_event = multiprocessing.Event()
server = Server(shutdown_event)
multiprocessing.Process(target=server.serve).start()
time.sleep(5)
shutdown_event.set()
Но я хочу сохранить метод Server.shutdown
вместо изменения интерфейса Server
в соответствии с его использованием (многопроцессорная обработка с одной обработкой в.), и я не хочу, чтобы клиенты имели дело с multiprocessing.Event
.
Наконец-то я нашел решение самостоятельно:
import multiprocessing
import time
class Server:
def __init__(self):
self.shutdown_event = multiprocessing.Event()
def serve(self):
print("serving")
while not self.shutdown_event.is_set():
print("hello")
time.sleep(1)
print("done")
def shutdown(self):
print("stopping")
self.shutdown_event.set()
if __name__ == "__main__":
server = Server()
multiprocessing.Process(target=server.serve).start()
time.sleep(5)
server.shutdown()
Он работает в любом случае: одиночная обработка (многопоточность) и многопроцессорность.
Примечание. — с multiprocessing.Event()
в методе __init__
экземпляры Server
больше не выбираются. Это может быть проблемой, если кто-то хочет вызвать экземпляр Server
в пуле процессов (с помощью multiprocessing.pool.Pool
или concurrent.futures.ProcessPoolExecutor
). В этом случае следует заменить multiprocessing.Event()
на multiprocessing.Manager().Event()
в методе __init__
.