简体   繁体   English

键盘中断与python的多处理

[英]Keyboard Interrupt with python's multiprocessing

I am having an issues gracefully handling a Keyboard Interrupt with python's multi-processing 我正在使用python的多处理优雅地处理键盘中断问题

(Yes I know that Ctr-C should not guarantee a graceful shutdown -- but lets leave that discussion for a different thread) (是的,我知道Ctr-C不应该​​保证正常关闭 - 但是让我们讨论一个不同的线程)

Consider the following code, where I am user a multiprocessing.Manager#list() which is a ListProxy which I understood handles multi-process access to a list. 考虑以下代码,其中我是用户multiprocessing.Manager#list() #list multiprocessing.Manager#list() ,它是一个ListProxy,我理解它处理对列表的多进程访问。

When I Ctr-C out of this -- I get a socket.error: [Errno 2] No such file or directory when trying to access the ListProxy 当我Ctr-C出来的时候 - 我得到一个socket.error: [Errno 2] No such file or directory尝试访问ListProxy时socket.error: [Errno 2] No such file or directory

I would love to have the shared list not to be corrupted upon Ctr-C. 我希望共享列表不会在Ctr-C上被破坏。 Is this possible?! 这可能吗?!

Note: I want to solve this without using Pools and Queues. 注意:我想在不使用池和队列的情况下解决此问题。

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

If you run that and then hit Ctr-C, we get the following: 如果你运行它,然后点击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

(Here is another approach using a multiprocessing.Lock with similar affect ... gist ) Similar Questions: (这是使用multiprocessing.Lock另一种方法。具有类似影响的方法... gist )类似的问题:

multiprocessing.Manager() fires up a child process which is responsible for handling your shared list proxy. multiprocessing.Manager()触发一个负责处理共享列表代理的子进程。

netstat output while running: 运行时的netstat输出:

unix 2 [ ACC ] STREAM LISTENING 3921657 8457/python /tmp/pymp-B9dcij/listener-X423Ml unix 2 [ACC] STREAM LISTENING 3921657 8457 / python / tmp / pymp-B9dcij / listener-X423Ml

this child process created by multiprocessing.Manager() is catching your SIGINT and exiting causing anything related to it to be dereferenced hence your "no such file" error (I also got several other errors depending on when i decided to send SIGINT). 这个由multiprocessing.Manager()创建的子进程捕获你的SIGINT并退出,导致与它相关的任何东西被解除引用,因此你的“没有这样的文件”错误(我还有其他几个错误取决于我何时决定发送SIGINT)。

to solve this you may directly declare a SyncManager object (instead of letting Manager() do it for you). 要解决此问题,您可以直接声明SyncManager对象(而不是让Manager()为您执行此操作)。 this will require you to use the start() method to actually fire up the child process. 这将要求您使用start()方法实际启动子进程。 the start() method takes an initialization function as its first argument (you can override SIGINT for the manager here). start()方法将初始化函数作为其第一个参数(您可以在此处覆盖管理器的SIGINT)。

code below, give this a try: 下面的代码,尝试一下:

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

As I answer on similar question ( duplicate ): 当我回答类似的问题( 重复 ):

Simplest solution - start manager with 最简单的解决方案 - 启动经理

manager.start(signal.signal, (signal.SIGINT, signal.SIG_IGN))

instead of manager.start(). 而不是manager.start()。 And check if signal module is in your imports (import signal). 并检查信号模块是否在您的导入(导入信号)中。

This catch and ignore SIGINT (Ctrl-C) in manager process. 这会捕获并忽略管理器进程中的SIGINT(Ctrl-C)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM