पायथन प्रक्रिया पूल गैर-शैतानी?


96

क्या एक अजगर पूल बनाना संभव होगा जो गैर-डेमोनिक हो? मैं एक ऐसा पूल चाहता हूं जो एक फ़ंक्शन को कॉल करने में सक्षम हो, जिसमें एक और पूल है।

मैं ऐसा चाहता हूं क्योंकि बहरीन प्रक्रियाएं प्रक्रिया नहीं बना सकती हैं। विशेष रूप से, यह त्रुटि का कारण होगा:

AssertionError: daemonic processes are not allowed to have children

उदाहरण के लिए, उस परिदृश्य पर विचार करें function_aजिसमें एक पूल है जो चलता है function_bजिसमें एक पूल है जो चलता है function_c। यह फ़ंक्शन श्रृंखला विफल हो जाएगी, क्योंकि function_bडेमॉन प्रक्रिया में चलाया जा रहा है, और डेमन प्रक्रियाएँ नहीं बना सकती हैं।


AFAIK, नहीं, यह संभव नहीं है कि पूल में सभी कार्यकर्ता का नियमन किया जाए और निर्भरता को इंजेक्ट करना संभव नहीं है , BTW मैं आपके प्रश्न के दूसरे भाग को नहीं समझता हूं I want a pool to be able to call a function that has another pool insideऔर इस तथ्य के साथ हस्तक्षेप कैसे करता है कि श्रमिकों को निष्क्रिय कर दिया गया है।
मौद

4
क्योंकि अगर फ़ंक्शन में एक पूल है जो फ़ंक्शन बी चलाता है जिसमें एक पूल है जो फ़ंक्शन सी चलाता है, तो बी में एक समस्या है कि इसे डेमन प्रक्रिया में चलाया जा रहा है, और डेमॉन प्रक्रियाएं प्रक्रियाएं नहीं बना सकती हैं। AssertionError: daemonic processes are not allowed to have children
मैक्स

जवाबों:


118

multiprocessing.pool.Poolवर्ग अपनी में कार्यकर्ता प्रक्रियाओं बनाता __init__विधि, उन्हें भूत का बनाता है और उन्हें शुरू होता है, और यह उनकी फिर से स्थापित करने के लिए संभव नहीं है daemonकरने के लिए विशेषता Falseवे शुरू कर रहे हैं इससे पहले कि (और बाद में इसे अब और अनुमति नहीं है)। लेकिन आप अपनी खुद की उप-क्लास बना सकते हैं multiprocesing.pool.Pool( multiprocessing.Poolकेवल एक रैपर फ़ंक्शन है) और अपनी खुद की multiprocessing.Processउप-कक्षा को प्रतिस्थापित करें , जो हमेशा गैर-डीमैनिक है, जिसका उपयोग कार्यकर्ता प्रक्रियाओं के लिए किया जाना है।

यह कैसे करना है, इसका एक पूर्ण उदाहरण यहां दिया गया है। महत्वपूर्ण भाग दो कक्षाएं हैं NoDaemonProcessऔर MyPoolसबसे ऊपर और अंत में कॉल करने के लिए pool.close()और pool.join()आपके MyPoolउदाहरण पर।

#!/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()

1
मैंने अभी अपने कोड को फिर से पायथन 2.7 / 3.2 (लिनक्स में "प्रिंट" लाइनों को ठीक करने के बाद) के साथ परीक्षण किया और OS X पर पायथन 2.6 / 2.7 / 3.2 OS X. लिनक्स और पायथन 2.7 / 3.2 ठीक काम करता है, लेकिन कोड वास्तव में साथ लटका हुआ है पायथन 2.6 ओएस एक्स (शेर) पर। यह मल्टीप्रोसेसिंग मॉड्यूल में एक बग प्रतीत होता है, जो निश्चित हो गया, लेकिन मैंने वास्तव में बग ट्रैकर की जांच नहीं की है।
क्रिस अरंड्ट सेप

1
धन्यवाद! खिड़कियों पर आपको कॉल करने की भी आवश्यकता हैmultiprocessing.freeze_support()
frmdstryr

2
अच्छा काम। अगर किसी को इस कोशिश के साथ "रिसाव (MyPool (प्रक्रियाओं = num_cpu)) को पूल के रूप में उपयोग करने के साथ मेमोरी लीक हो रही है:" पूल को ठीक से निपटाने के लिए
क्रिस लूसियन

31
MyPoolडिफ़ॉल्ट के बजाय उपयोग करने के नुकसान क्या हैं Pool? दूसरे शब्दों में, बाल प्रक्रियाओं को शुरू करने के लचीलेपन के बदले, मुझे क्या कीमत चुकानी होगी? (यदि कोई लागत नहीं थी, Poolतो निश्चित रूप से मानक ने गैर-डायमोनिक प्रक्रियाओं का उपयोग किया होगा)।
अधिकतम

4
@ माचेन हां, दुर्भाग्य से यह सच है। पायथन 3.6 में Poolकक्षा को बड़े पैमाने पर प्रतिबिंबित किया गया है, इसलिए Processअब एक सरल विशेषता नहीं है, लेकिन एक विधि, जो एक संदर्भ से प्राप्त होने वाली प्रक्रिया का उदाहरण देती है । मैंने एक NoDaemonPoolउदाहरण वापस करने के लिए इस पद्धति को अधिलेखित करने की कोशिश की , लेकिन AssertionError: daemonic processes are not allowed to have childrenजब पूल का उपयोग किया जाता है तो यह अपवाद होता है।
क्रिस अरंड्ट

26

मुझे पायथन 3.7 में एक गैर-डेमोनिक पूल को नियोजित करने की आवश्यकता थी और स्वीकार किए गए उत्तर में पोस्ट किए गए कोड को समाप्त करना था। नीचे स्निपेट है जो गैर-डायमोनिक पूल बनाता है:

class NoDaemonProcess(multiprocessing.Process):
    @property
    def daemon(self):
        return False

    @daemon.setter
    def daemon(self, value):
        pass


class NoDaemonContext(type(multiprocessing.get_context())):
    Process = NoDaemonProcess

# 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):
    def __init__(self, *args, **kwargs):
        kwargs['context'] = NoDaemonContext()
        super(MyPool, self).__init__(*args, **kwargs)

जैसा कि वर्तमान कार्यान्वयन multiprocessingबड़े पैमाने पर संदर्भों पर आधारित होने के लिए रिफलेक्ट किया गया है, हमें एक NoDaemonContextवर्ग प्रदान करने की आवश्यकता है, जिसमें हमारी NoDaemonProcessविशेषता है। MyPoolतब डिफ़ॉल्ट के बजाय उस संदर्भ का उपयोग करेगा।

उस ने कहा, मुझे चेतावनी देनी चाहिए कि इस दृष्टिकोण के लिए कम से कम 2 चेतावनी हैं:

  1. यह अभी भी multiprocessingपैकेज के कार्यान्वयन विवरण पर निर्भर करता है , और इसलिए किसी भी समय टूट सकता है।
  2. वैध कारण हैं कि multiprocessingगैर-डायमोनिक प्रक्रियाओं का उपयोग करना इतना कठिन क्यों बना, जिनमें से कई यहां बताए गए हैं । मेरी राय में सबसे सम्मोहक है:

    सबप्रोसेस के इस्तेमाल से बच्चों के थ्रेड्स को बंद करने की अनुमति देने के रूप में, अगर 'या तो माता-पिता या बच्चे थ्रेड्स की छोटी सेना बनाने का जोखिम उठाते हैं, तो या तो माता-पिता या बच्चे थ्रेड्स सबप्रोसेस पूरा होने से पहले ही समाप्त हो जाते हैं और लौट जाते हैं।


कैविएट के बारे में: मेरा उपयोग मामला कार्यों को समानांतर कर रहा है, लेकिन भव्य-बच्चे अपने माता-पिता को जानकारी देते हैं कि बदले में कुछ आवश्यक स्थानीय प्रसंस्करण करने के बाद अपने माता-पिता को जानकारी वापस करें । नतीजतन, हर स्तर / शाखा को अपने सभी रिसावों के लिए एक स्पष्ट इंतजार है। क्या कैविट अभी भी लागू होता है यदि आपको स्पष्ट रूप से समाप्त प्रक्रियाओं के लिए इंतजार करना पड़ता है?
a_A

AttributeError: module 'multiprocessing' has no attribute 'pool'पायथन में त्रुटि हो रही है 3.8.0
Nyxynyx

@Nyxynyx मत भूलनाimport multiprocessing.pool
क्रिस Arndt

22

बहु मॉड्यूल प्रक्रियाओं के साथ पूल उपयोग करने के लिए एक अच्छा इंटरफ़ेस है या धागे। आपके वर्तमान उपयोग के मामले के आधार पर, आप multiprocessing.pool.ThreadPoolअपने बाहरी पूल के लिए उपयोग करने पर विचार कर सकते हैं , जिसके परिणामस्वरूप प्रक्रियाओं के विपरीत थ्रेड्स (जो भीतर से प्रक्रियाओं को फैलाने की अनुमति देते हैं) होंगे

यह जीआईएल द्वारा सीमित हो सकता है, लेकिन मेरे विशेष मामले में (मैंने दोनों का परीक्षण किया है) , बाहरी से प्रक्रियाओं के लिए स्टार्टअप समय के Poolरूप में यहां बनाया गया समाधान के साथ आगे निकल गया ThreadPool


यह वास्तव में स्वैप करने के लिए आसान है Processesके लिए Threadsयहां या यहांThreadPool समाधान का उपयोग करने के तरीके के बारे में और पढ़ें ।


धन्यवाद - इससे मुझे बहुत मदद मिली - यहाँ थ्रेडिंग का बढ़िया उपयोग (स्पॉन प्रक्रियाओं को जो वास्तव में अच्छा प्रदर्शन करता है)
trance_dude

1
व्यावहारिक समाधान की तलाश कर रहे लोगों के लिए जो शायद उनकी स्थिति पर लागू होता है, यह एक है।
अबनाना

6

कुछ पायथन संस्करणों पर मानक पूल की जगह रीति रिवाज त्रुटि उत्पन्न कर सकते हैं AssertionError: group argument must be None for now:।

यहाँ मुझे एक समाधान मिला जो मदद कर सकता है:

class NoDaemonProcess(multiprocessing.Process):
    # make 'daemon' attribute always return False
    @property
    def daemon(self):
        return False

    @daemon.setter
    def daemon(self, val):
        pass


class NoDaemonProcessPool(multiprocessing.pool.Pool):

    def Process(self, *args, **kwds):
        proc = super(NoDaemonProcessPool, self).Process(*args, **kwds)
        proc.__class__ = NoDaemonProcess

        return proc

4

concurrent.futures.ProcessPoolExecutorयह सीमा नहीं है। इसमें बिना किसी समस्या के एक नेस्टेड प्रक्रिया पूल हो सकता है:

from concurrent.futures import ProcessPoolExecutor as Pool
from itertools import repeat
from multiprocessing import current_process
import time

def pid():
    return current_process().pid

def _square(i):  # Runs in inner_pool
    square = i ** 2
    time.sleep(i / 10)
    print(f'{pid()=} {i=} {square=}')
    return square

def _sum_squares(i, j):  # Runs in outer_pool
    with Pool(max_workers=2) as inner_pool:
        squares = inner_pool.map(_square, (i, j))
    sum_squares = sum(squares)
    time.sleep(sum_squares ** .5)
    print(f'{pid()=}, {i=}, {j=} {sum_squares=}')
    return sum_squares

def main():
    with Pool(max_workers=3) as outer_pool:
        for sum_squares in outer_pool.map(_sum_squares, range(5), repeat(3)):
            print(f'{pid()=} {sum_squares=}')

if __name__ == "__main__":
    main()

उपरोक्त प्रदर्शन कोड का परीक्षण पायथन 3.8 के साथ किया गया था।

क्रेडिट: jfs द्वारा उत्तर


1
यह अब स्पष्ट रूप से सबसे अच्छा समाधान है, क्योंकि इसमें न्यूनतम परिवर्तनों की आवश्यकता होती है।
ड्रीमफ्लैशर

1
अच्छी तरह से काम! ... एक बच्चे के रूप में एक साइड-नोट के रूप में- multiprocessing.Poolअंदर ProcessPoolExecutor.Poolभी संभव है!
राफेल

3

मुझे जो समस्या आई थी वह मॉड्यूल के बीच ग्लोबल्स को आयात करने की कोशिश में थी, जिसके कारण प्रोसेसपूल () लाइन कई बार मूल्यांकन किया गया था।

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

फिर अपने कोड में कहीं और से सुरक्षित आयात करें

from globals import Globals
Globals().shared_manager      
Globals().shared_process_pool
Globals().shared_thread_pool  
Globals().shared_lock         

2

मैंने लोगों को इस समस्या से निपटने celeryके लिए multiprocessingकहा जाता है जिसे बिलियार्ड (मल्टीप्रोसेसिंग पूल एक्सटेंशन) का कांटा कहा जाता है , जो डेमोनिक प्रक्रियाओं को बच्चों को पालने की अनुमति देता है। चलना केवल multiprocessingमॉड्यूल को बदलने के लिए है:

import billiard as multiprocessing
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.