Python Process Pool non-daemonic?


¿Sería posible crear un pool de python que no sea daemónico? Quiero que una piscina sea capaz de llamar a una función que tiene otra piscina dentro.

Quiero esto porque los procesos deamon no pueden crear procesos. Específicamente, causará el error:

AssertionError: daemonic processes are not allowed to have children

Por ejemplo, considere el escenario donde function_a tiene un pool que se ejecuta function_b que tiene un pool que se ejecuta function_c. Esta cadena de funciones fallará, porque function_b se está ejecutando en un proceso daemon, y los procesos daemon no pueden crear procesos.

Author: Seanny123, 2011-08-07

3 answers

La clase multiprocessing.pool.Pool crea los procesos worker en su método __init__, los hace demoníacos y los inicia, y no es posible volver a establecer su atributo daemon a False antes de que se inicien (y después ya no está permitido). Pero usted puede crear su propia sub-clase de multiprocesing.pool.Pool (multiprocessing.Pool es solo una función de envoltura) y sustituye tu propia subclase multiprocessing.Process, que siempre es no daemónica, para ser utilizada para los procesos worker.

Aquí hay un ejemplo completo de cómo hacer esto. El las partes importantes son las dos clases NoDaemonProcess y MyPool en la parte superior y llamar pool.close() y pool.join() en su instancia MyPool al final.

#!/usr/bin/env python
# -*- coding: UTF-8 -*-

import multiprocessing
# We must import this explicitly, it is not imported by the top-level
# multiprocessing module.
import multiprocessing.pool
import time

from random import randint


class NoDaemonProcess(multiprocessing.Process):
    # make 'daemon' attribute always return False
    def _get_daemon(self):
        return False
    def _set_daemon(self, value):
        pass
    daemon = property(_get_daemon, _set_daemon)

# We sub-class multiprocessing.pool.Pool instead of multiprocessing.Pool
# because the latter is only a wrapper function, not a proper class.
class MyPool(multiprocessing.pool.Pool):
    Process = NoDaemonProcess

def sleepawhile(t):
    print("Sleeping %i seconds..." % t)
    time.sleep(t)
    return t

def work(num_procs):
    print("Creating %i (daemon) workers and jobs in child." % num_procs)
    pool = multiprocessing.Pool(num_procs)

    result = pool.map(sleepawhile,
        [randint(1, 5) for x in range(num_procs)])

    # The following is not really needed, since the (daemon) workers of the
    # child's pool are killed when the child is terminated, but it's good
    # practice to cleanup after ourselves anyway.
    pool.close()
    pool.join()
    return result

def test():
    print("Creating 5 (non-daemon) workers and jobs in main process.")
    pool = MyPool(5)

    result = pool.map(work, [randint(1, 5) for x in range(5)])

    pool.close()
    pool.join()
    print(result)

if __name__ == '__main__':
    test()
 78
Author: Chris Arndt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-09-30 14:14:37

El módulo multiprocesamiento tiene una interfaz agradable para usar pools con subprocesos de procesos o. Dependiendo de su caso de uso actual, puede considerar usar multiprocessing.pool.ThreadPool para su Grupo externo, lo que resultará en subprocesos (que permiten generar procesos desde dentro) en lugar de procesos.

Podría estar limitado por el GIL, pero en mi caso particular (Probé ambos) , el tiempo de inicio para los procesos desde el exterior Pool como creado aquí mucho más que la solución con ThreadPool.


Es muy fácil cambiar Processes por Threads. Lea más sobre cómo usar una solución ThreadPool aquí o aquí.

 8
Author: timmwagener,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:54:37

El problema que encontré fue al intentar importar globales entre módulos, causando que la línea ProcessPool() se evaluara varias veces.

Globals.py

from processing             import Manager, Lock
from pathos.multiprocessing import ProcessPool
from pathos.threading       import ThreadPool

class SingletonMeta(type):
    def __new__(cls, name, bases, dict):
        dict['__deepcopy__'] = dict['__copy__'] = lambda self, *args: self
        return super(SingletonMeta, cls).__new__(cls, name, bases, dict)

    def __init__(cls, name, bases, dict):
        super(SingletonMeta, cls).__init__(name, bases, dict)
        cls.instance = None

    def __call__(cls,*args,**kw):
        if cls.instance is None:
            cls.instance = super(SingletonMeta, cls).__call__(*args, **kw)
        return cls.instance

    def __deepcopy__(self, item):
        return item.__class__.instance

class Globals(object):
    __metaclass__ = SingletonMeta
    """     
    This class is a workaround to the bug: AssertionError: daemonic processes are not allowed to have children

    The root cause is that importing this file from different modules causes this file to be reevalutated each time, 
    thus ProcessPool() gets reexecuted inside that child thread, thus causing the daemonic processes bug    
    """
    def __init__(self):
        print "%s::__init__()" % (self.__class__.__name__)
        self.shared_manager      = Manager()
        self.shared_process_pool = ProcessPool()
        self.shared_thread_pool  = ThreadPool()
        self.shared_lock         = Lock()        # BUG: Windows: global name 'lock' is not defined | doesn't affect cygwin

Luego importa de forma segura desde cualquier parte de tu código

from globals import Globals
Globals().shared_manager      
Globals().shared_process_pool
Globals().shared_thread_pool  
Globals().shared_lock         
 0
Author: James McGuigan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-24 20:50:01