【问题标题】:Python custom signal handling in processes pool进程池中的 Python 自定义信号处理
【发布时间】:2018-07-11 21:32:23
【问题描述】:

我正在处理以下问题:

我已经实现了一个虚拟的“Thing”类,它会休眠 10 秒并记录一条消息(“foo”)。此类在 Processes Pool 的工作函数中实例化,并调用实现上述逻辑的 'foo' 方法。

我想要实现的是自定义的信号处理:只要进程没有终止,如果发送了CTRL+C(SIGINT),每个进程都会记录信号并立即终止。

一半的逻辑在工作:当每个进程都在休眠时,在 SIGINT 上,它们将被中断并且池将被关闭。

问题:如果所有进程都成功结束并发送了 SIGINT,消息将被记录但池不会关闭。

代码:

import logging
import signal
import os
import time

from multiprocessing import Pool, current_process


logger = logging.getLogger('test')

SIGNAL_NAMES = dict((k, v) for v, k in reversed(sorted(signal.__dict__.items()))
                    if v.startswith('SIG') and not v.startswith('SIG_'))


class Thing(object):
    def __init__(self, my_id):
        self.my_id = my_id
        self.logger = logging.getLogger(str(my_id))

    def foo(self):
        time.sleep(10)
        self.logger.info('[%s] Foo after 10 secs!', self.my_id)


class Daemon(object):
    def __init__(self, no_processes, max_count):
        signal.signal(signal.SIGINT, self.stop)

        self.done = False
        self.count = 0
        self.max_count = max_count
        self.pool = Pool(no_processes, initializer=self.pool_initializer)

    def stop(self, signum, _):
        """ Stop function for Daemon """
        sig = SIGNAL_NAMES.get(signum) or signum
        logger.info('[Daemon] Stopping (received signal %s', sig)
        self.done = True

    def _generate_ids(self):
        """ Generator function of the IDs for the Processes Pool """
        while not self.done:
            if self.count < self.max_count:
                my_id = "ID-{}".format(self.count)
                logger.info('[Daemon] Generated ID %s', my_id)
                time.sleep(3)
                yield my_id
                self.count += 1
        time.sleep(1)

    def run(self):
        """ Main daemon run function """
        pid = os.getpid()
        logger.info('[Daemon] Started running on PID %s', str(pid))
        my_ids = self._generate_ids()

        for res in self.pool.imap_unordered(run_thing, my_ids):
            logger.info("[Daemon] Finished %s", res or '')

        logger.info('[Daemon] Closing & waiting processes to terminate')
        self.pool.close()
        self.pool.join()

    def pool_initializer(self):
        """ Pool initializer function """
        signal.signal(signal.SIGINT, self.worker_signal_handler)

    @staticmethod
    def worker_signal_handler(signum, _):
        """ Signal handler for the Process worker """
        sig = SIGNAL_NAMES.get(signum) or signum
        cp = current_process()
        logger.info("[%s] Received in worker %s signal %s", WORKER_THING_ID or '', str(cp), sig)

        global WORKER_EXITING
        WORKER_EXITING = True


WORKER_EXITING = False
WORKER_THING_ID = None


def run_thing(arg):
    """ Worker function for processes """
    if WORKER_EXITING:
        return

    global WORKER_THING_ID
    WORKER_THING_ID = arg
    run_exception = None

    logger.info('[%s] START Thing foo-ing', arg)
    logging.getLogger('Thing-{}'.format(arg)).setLevel(logging.INFO)
    try:
        thing = Thing(arg)
        thing.foo()
    except Exception as e:
        run_exception = e
    finally:
        WORKER_THING_ID = None
    logger.info('[%s] STOP Thing foo-ing', arg)

    if run_exception:
        logger.error('[%s] EXCEPTION on Thing foo-ing: %s', arg, run_exception)

    return arg


if __name__ == '__main__':
    logging.basicConfig()
    logger.setLevel(logging.INFO)
    daemon = Daemon(4, 3)
    daemon.run()

【问题讨论】:

    标签: python process signals signal-handling


    【解决方案1】:

    您的问题是函数_generate_ids() 中的逻辑。函数永远不会结束,所以pool.imap_unordered()永远不会自己结束,只需要被CTRL-C打断。

    把它改成这样:

    def _generate_ids(self):
        """ Generator function of the IDs for the Processes Pool """
    
        for i in range(self.max_count):
            time.sleep(3)
            my_id = "ID-{}".format(self.count)
            logger.info('[Daemon] Generated ID %s', my_id)
            if self.done:
                break
            self.count += 1
            yield my_id
    

    并且进程正常结束。

    【讨论】:

    • 我向您展示的代码是真实代码的虚拟版本。 while not self.done 必须在那里,生成器实际上在其范围内做了很多事情。所以生成器的要求听起来像这样:无限循环,在循环中做一些事情,只有在self.done 为真时才结束。如您所见,Daemon 类的stop 方法是父进程的信号处理程序,但它从未被调用。
    猜你喜欢
    • 1970-01-01
    • 2011-04-10
    • 2017-11-30
    • 1970-01-01
    • 1970-01-01
    • 2022-01-08
    • 2012-09-23
    • 2022-06-14
    • 2016-04-05
    相关资源
    最近更新 更多