我遇到了使用python的多处理
优雅地处理键盘中断的问题(是的,我知道Ctr-C不应该保证正常关机 - 但是让我们讨论一个不同的线程)
考虑以下代码,我是用户multiprocessing.Manager#list()
,这是一个ListProxy,我理解它处理对列表的多进程访问。
当我从中获取Ctr-C时 - 在尝试访问ListProxy时我得到socket.error: [Errno 2] No such file or directory
我希望共享列表不会在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)
类似的问题:
答案 0 :(得分:16)
multiprocessing.Manager()会启动一个负责处理共享列表代理的子进程。
运行时的netstat输出:
unix 2 [ACC] STREAM LISTENING 3921657 8457 / python 的/ tmp / pymp-B9dcij /听众-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
答案 1 :(得分:0)
当我回答类似问题时(duplicate):
最简单的解决方案 - 使用
启动经理manager.start(signal.signal, (signal.SIGINT, signal.SIG_IGN))
而不是manager.start()。 并检查信号模块是否在您的导入(导入信号)中。
此捕获并忽略管理器进程中的SIGINT(Ctrl-C)。