Прерывание клавиатуры с многопроцессорной обработкой Python
У меня возникли проблемы изящной обработки прерывания клавиатуры с мульти-обработки Python
(Да, я знаю, что Ctr-C не должен гарантировать постепенное отключение - но давайте оставим это обсуждение для другой темы)
Рассмотрим следующий код, где я являюсь пользователем multiprocessing.Manager#list()
который является ListProxy, который, как я понял, обрабатывает многопроцессный доступ к списку.
Когда я Ctr-C из этого - я получаю socket.error: [Errno 2] No such file or directory
при попытке получить доступ к ListProxy
Я хотел бы, чтобы общий список не был поврежден при Ctr-C. Это возможно?!
Примечание: я хочу решить эту проблему без использования пулов и очередей.
from multiprocessing import Process, Manager
from time import sleep
def f(process_number, shared_array):
try:
print "starting thread: ", process_number
shared_array.append(process_number)
sleep(3)
shared_array.append(process_number)
except KeyboardInterrupt:
print "Keyboard interrupt in process: ", process_number
finally:
print "cleaning up thread", process_number
if __name__ == '__main__':
processes = []
manager = Manager()
shared_array = manager.list()
for i in xrange(4):
p = Process(target=f, args=(i, shared_array))
p.start()
processes.append(p)
try:
for process in processes:
process.join()
except KeyboardInterrupt:
print "Keyboard interrupt in main"
for item in shared_array:
# raises "socket.error: [Errno 2] No such file or directory"
print item
Если вы запустите это, а затем нажмете Ctr-C, мы получим следующее:
starting thread: 0
starting thread: 1
starting thread: 3
starting thread: 2
^CKeyboard interupt in process: 3
Keyboard interupt in process: 0
cleaning up thread 3
cleaning up thread 0
Keyboard interupt in process: 1
Keyboard interupt in process: 2
cleaning up thread 1
cleaning up thread 2
Keyboard interupt in main
Traceback (most recent call last):
File "multi.py", line 33, in <module>
for item in shared_array:
File "<string>", line 2, in __getitem__
File "/usr/local/Cellar/python/2.7.3/Frameworks/Python.framework/Versions/2.7/lib/python2.7/multiprocessing/managers.py", line 755, in _callmethod
self._connect()
File "/usr/local/Cellar/python/2.7.3/Frameworks/Python.framework/Versions/2.7/lib/python2.7/multiprocessing/managers.py", line 742, in _connect
conn = self._Client(self._token.address, authkey=self._authkey)
File "/usr/local/Cellar/python/2.7.3/Frameworks/Python.framework/Versions/2.7/lib/python2.7/multiprocessing/connection.py", line 169, in Client
c = SocketClient(address)
File "/usr/local/Cellar/python/2.7.3/Frameworks/Python.framework/Versions/2.7/lib/python2.7/multiprocessing/connection.py", line 293, in SocketClient
s.connect(address)
File "/usr/local/Cellar/python/2.7.3/Frameworks/Python.framework/Versions/2.7/lib/python2.7/socket.py", line 224, in meth
return getattr(self._sock,name)(*args)
socket.error: [Errno 2] No such file or directory
(Вот еще один подход, использующий multiprocessing.Lock
с похожим аффектом... gist) Похожие вопросы:
2 ответа
multiprocessing.Manager() запускает дочерний процесс, который отвечает за обработку вашего прокси общего списка.
вывод netstat во время работы:
unix 2 [ACC] LISTENING STREAM 3921657 8457/python /tmp/pymp-B9dcij/listener-X423Ml
этот дочерний процесс, созданный multiprocessing.Manager() перехватывает ваш SIGINT и завершает работу, вызывая разыменование всего, что с ним связано, и, следовательно, вашу ошибку "нет такого файла" (я также получил несколько других ошибок в зависимости от того, когда я решил отправить SIGINT).
Чтобы решить эту проблему, вы можете напрямую объявить объект SyncManager (вместо того, чтобы позволить Manager () сделать это за вас). это потребует от вас использования метода start () для фактического запуска дочернего процесса. метод start () принимает функцию инициализации в качестве первого аргумента (вы можете переопределить SIGINT для менеджера здесь).
код ниже, попробуйте:
from multiprocessing import Process, Manager
from multiprocessing.managers import BaseManager, SyncManager
from time import sleep
import signal
#handle SIGINT from SyncManager object
def mgr_sig_handler(signal, frame):
print 'not closing the mgr'
#initilizer for SyncManager
def mgr_init():
signal.signal(signal.SIGINT, mgr_sig_handler)
#signal.signal(signal.SIGINT, signal.SIG_IGN) # <- OR do this to just ignore the signal
print 'initialized mananger'
def f(process_number, shared_array):
try:
print "starting thread: ", process_number
shared_array.append(process_number)
sleep(3)
shared_array.append(process_number)
except KeyboardInterrupt:
print "Keyboard interrupt in process: ", process_number
finally:
print "cleaning up thread", process_number
if __name__ == '__main__':
processes = []
#using syncmanager directly instead of letting Manager() do it for me
manager = SyncManager()
manager.start(mgr_init) #fire up the child manager process
shared_array = manager.list()
for i in xrange(4):
p = Process(target=f, args=(i, shared_array))
p.start()
processes.append(p)
try:
for process in processes:
process.join()
except KeyboardInterrupt:
print "Keyboard interrupt in main"
for item in shared_array:
print item
Как я отвечу на аналогичный вопрос ( дубликат):
Самое простое решение - начать менеджер с
manager.start(signal.signal, (signal.SIGINT, signal.SIG_IGN))
вместо manager.start(). И проверьте, есть ли сигнальный модуль в вашем импорте (импорт сигнала).
Это ловит и игнорирует SIGINT (Ctrl-C) в процессе менеджера.