পাইথন প্রক্রিয়া পুল নন-ডেমনিক?


102

অ-ডেমনিক নয় এমন একটি অজগর পুল তৈরি করা কি সম্ভব হবে? আমি চাই যে একটি পুল একটি ফাংশন কল করতে সক্ষম হবে যার ভিতরে আরও একটি পুল রয়েছে।

আমি এটি চাই কারণ ডিমন প্রক্রিয়াগুলি প্রক্রিয়া তৈরি করতে পারে না। বিশেষত, এটি ত্রুটির কারণ ঘটবে:

AssertionError: daemonic processes are not allowed to have children

উদাহরণস্বরূপ, দৃশ্যকল্প যেখানে বিবেচনা function_aএকটি পুল যা রান করেছেন function_bযা পুল যার রান হয়েছে function_c। এই ফাংশন চেইনটি ব্যর্থ হবে, কারণ function_bএকটি ডেমন প্রক্রিয়াতে চলছে এবং ডেমন প্রক্রিয়াগুলি প্রক্রিয়া তৈরি করতে পারে না।


আফাইক, না এটি সম্ভব নয় যে পুলের সমস্ত কর্মী ডিমনাইজড এবং নির্ভরতা ইনজেকশন করা সম্ভব নয়, বিটিডাব্লু আমি আপনার প্রশ্নের দ্বিতীয় অংশটি 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
সর্বোচ্চ

উত্তর:


122

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()

4
আমি লিনাক্স এবং পাইথন ২.6 / ২.7 / ৩.২ ওএস এক্স-তে পাইথন ২.7 / ৩.২ ("প্রিন্ট" লাইনগুলি ঠিক করার পরে) দিয়ে আবার আমার কোডটি পরীক্ষা করেছিলাম এবং লিনাক্স এবং ওএস এক্স-তে পাইথন ২.7 / ৩.২ ঠিকঠাক কাজ করে তবে কোডটি প্রকৃতপক্ষে স্তব্ধ হয় না ওএস এক্স (সিংহ) এ পাইথন ২.6। এটি মাল্টিপ্রসেসিং মডিউলে একটি বাগ বলে মনে হচ্ছে, যা স্থির হয়ে গেছে, তবে আমি আসলে বাগ ট্র্যাকারটি পরীক্ষা করে দেখিনি।
ক্রিস আরেন্ড্ট

4
ধন্যবাদ! উইন্ডোজগুলিতে আপনাকে কল করতে হবেmultiprocessing.freeze_support()
frmdstryr

4
চমৎকার কাজ. যদি কেউ এর সাথে স্মৃতি ফাঁস হয়ে যায় তবে পুল সহ "ক্লোজিং (মাইপুল (প্রক্রিয়াগুলি = num_cpu)) ব্যবহার করুন:" পুলটি সঠিকভাবে নিষ্পত্তি করতে
ক্রিস লুসিয়ান

32
MyPoolডিফল্ট পরিবর্তে ব্যবহার করার অসুবিধাগুলি কী Pool? অন্য কথায়, শিশু প্রক্রিয়াগুলি শুরু করার নমনীয়তার বিনিময়ে, আমি কোন মূল্য দিতে পারি? (যদি কোনও ব্যয় না ঘটে, সম্ভবত মানটি Poolডি-ডেমোনিক প্রক্রিয়া ব্যবহার করত)।
সর্বোচ্চ

4
@ মাচেন হ্যাঁ, দুর্ভাগ্যক্রমে এটি সত্য। পাইথন ৩.6-এ Poolক্লাসটি বিস্তৃতভাবে রিফ্যাক্টর করা হয়েছে, সুতরাং Processএটি কোনও সাধারণ বৈশিষ্ট্য নয়, তবে একটি পদ্ধতি, যা প্রসঙ্গ থেকে প্রাপ্ত প্রক্রিয়াটির উদাহরণ দেয় । NoDaemonPoolউদাহরণটি ফেরত দেওয়ার জন্য আমি এই পদ্ধতিটি ওভাররাইট করার চেষ্টা করেছি , তবে AssertionError: daemonic processes are not allowed to have childrenপুলটি ব্যবহার করা হলে ব্যতিক্রম হয়।
ক্রিস আরেন্ড্ট

29

পাইথন ৩.7-এ একটি নন-ডিমনিক পুল নিয়োগ করার প্রয়োজনীয়তা আমার ছিল এবং গ্রহণযোগ্য উত্তরে পোস্ট করা কোডটি মানিয়ে শেষ করেছি ended নীচে স্নিপেট রয়েছে যা নন-ডিমনিক পুল তৈরি করে:

import multiprocessing.pool

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

যেহেতু বর্তমানের প্রয়োগটি প্রাসঙ্গিকগুলির multiprocessingউপর ভিত্তি করে বিস্তৃতভাবে রিফ্যাক্টর করা হয়েছে, NoDaemonContextতাই আমাদের এমন একটি শ্রেণি সরবরাহ করা দরকার যা আমাদের NoDaemonProcessবৈশিষ্ট্যযুক্ত।NestablePoolতারপরে ডিফল্টটির পরিবর্তে সেই প্রসঙ্গটি ব্যবহার করবে।

এটি বলেছিল, আমার সতর্ক করা উচিত যে এই পদ্ধতির কমপক্ষে দুটি ক্যাভেট রয়েছে:

  1. এটি এখনও বাস্তবায়ন বিবরণ উপর নির্ভর করে multiprocessing প্যাকেজ এবং তাই যে কোনও সময় ভেঙে যেতে পারে।
  2. multiprocessingনন-ডেমনিক প্রক্রিয়াগুলি ব্যবহার করা কেন এত কঠিন করে তার কার্যকর কারণ রয়েছে , যার অনেকগুলি এখানে ব্যাখ্যা করা হয়েছে । আমার মতে সর্বাধিক আকর্ষণীয়:

সাব-প্রসেস ব্যবহার করে বাচ্চাদের থ্রেডগুলি নিজস্ব বাচ্চাদের ছড়িয়ে দেওয়ার অনুমতি দেয় তবে পিতামাতা বা সন্তানের উভয় থ্রেড সমাপ্ত হলে সাব-প্রসেসটি শেষ হয়ে ফিরে আসার আগে যদি জম্বি 'নাতি-নাতনিদের' একটি সামান্য সেনা তৈরি করার ঝুঁকি থাকে।


4
সাবধানবাণী সম্পর্কে: আমার ব্যবহারের বিষয়টি সমান্তরাল কাজ, তবে গ্র্যান্ড-বাচ্চারা তাদের পিতামাতাকে তথ্য দেয় যে পরিবর্তে কিছু প্রয়োজনীয় স্থানীয় প্রক্রিয়াজাতকরণ করার পরে তাদের পিতামাতার কাছে তথ্য ফেরত দেয়। ফলস্বরূপ, প্রতিটি স্তরের / শাখার তার সমস্ত পাতার জন্য একটি স্পষ্ট অপেক্ষা আছে। যদি আপনার স্পষ্টভাবে স্প্যানড প্রক্রিয়াগুলি শেষ হওয়ার জন্য অপেক্ষা করতে হয় তবে কী সাবধানতা অবলম্বন করা হবে?
এ_এ

মাল্টিপ্রসেসিং.পুলের পরিবর্তে কীভাবে এটি ব্যবহার করবেন তা আপনি কী বিরক্ত করবেন?
রেডিও নিয়ন্ত্রিত

"আপনি এখন মাল্টিপ্রসেসিং.পুল এবং নেস্টেবলপুল আন্তঃবিন্যভাবে ব্যবহার করতে পারেন"।
রেডিও নিয়ন্ত্রিত

22

মাল্টিপ্রসেসিং মডিউল প্রক্রিয়া পুল ব্যবহার করার জন্য একটি চমৎকার ইন্টারফেস আছে বা থ্রেড। আপনার বর্তমান ব্যবহারের ক্ষেত্রে উপর নির্ভর করে আপনি multiprocessing.pool.ThreadPoolআপনার বাইরের পুলের জন্য ব্যবহার বিবেচনা করতে পারেন , যার ফলে থ্রেডগুলি (প্রক্রিয়াগুলিকে অভ্যন্তরীণভাবে প্রসারণ করতে দেয়) প্রক্রিয়াগুলির বিপরীতে পরিণত হবে।

এটি জিআইএল দ্বারা সীমাবদ্ধ হতে পারে তবে আমার বিশেষ ক্ষেত্রে (আমি উভয়ই পরীক্ষা করে দেখেছি) , বাহ্যিক থেকে প্রক্রিয়াগুলির সূচনাকালটি এখানেPool তৈরি হিসাবে সমাধানটি এতদূর ছাড়িয়ে গেছে ।ThreadPool


এটা সত্যিই swap 'র করা সহজ Processesজন্য Threads। কীভাবে ThreadPoolসমাধানটি এখানে বা এখানে ব্যবহার করা যায় সে সম্পর্কে আরও পড়ুন ।


ধন্যবাদ - এটি আমাকে অনেক সাহায্য করেছে - এখানে থ্রেডিংয়ের দুর্দান্ত ব্যবহার (প্রকৃত প্রক্রিয়াগুলি
উত্সাহিত

4
কোনও ব্যবহারিক সমাধানের সন্ধানকারী লোকদের জন্য যা সম্ভবত তাদের অবস্থার সাথে প্রযোজ্য, এটিই এটি।
অবনান

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 দিয়ে পরীক্ষা করা হয়েছিল।

ProcessPoolExecutorতবে এর একটি সীমাবদ্ধতা হ'ল এটি নেই maxtasksperchild। আপনার যদি এটির প্রয়োজন হয় তবে ম্যাসিমিলিয়ানো এর পরিবর্তে উত্তরটি বিবেচনা করুন ।

ক্রেডিট: জেএফএস দ্বারা উত্তর


4
এটি এখন স্পষ্টতই সেরা সমাধান, কারণ এতে ন্যূনতম পরিবর্তন প্রয়োজন।
ড্রিমফ্ল্যাশার

4
পুরোপুরি কাজ করে! ... একটি পার্শ্ব-লক্ষ্য করুন, একটি child- ব্যবহার multiprocessing.Poolভিতরে ProcessPoolExecutor.Poolসম্ভব!
রাফেল

4

আমি যে সমস্যার মুখোমুখি হয়েছি তা হ'ল মডিউলগুলির মধ্যে গ্লোবালগুলি আমদানির চেষ্টা করার ফলে প্রসেসপুল () লাইনটি একাধিকবার মূল্যায়ন করতে সক্ষম হয়েছিল।

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         

আমি pathos.multiprocessingএখানে আরও প্রসারিত র‌্যাপার ক্লাস লিখেছি :

পার্শ্ব নোট হিসাবে, যদি আপনার ব্যবহারের ক্ষেত্রে পারফরম্যান্স অপটিমাইজেশন হিসাবে কেবল অ্যাসিঙ্ক মাল্টিপ্রসেস ম্যাপের প্রয়োজন হয়, তবে জবলিব আপনার সমস্ত প্রক্রিয়া পুলগুলি পর্দার আড়ালে পরিচালনা করবে এবং এটি খুব সাধারণ বাক্য গঠনটিকে অনুমতি দেবে:

squares = Parallel(-1)( delayed(lambda num: num**2)(x) for x in range(100) )

3

আমি লোকজনকে বিলিয়ার্ড (মাল্টিপ্রসেসিং পুল এক্সটেনশন) নামক celeryকাঁটাচামচ ব্যবহার করে এই সমস্যাটি মোকাবেলা করতে দেখেছি , যা ডেমোনিক প্রক্রিয়াগুলি শিশুদের উত্সাহিত করতে দেয়। ওয়ালকারাউন্ডটি কেবল মডিউলটি এর মাধ্যমে প্রতিস্থাপন করবে :multiprocessingmultiprocessing

import billiard as multiprocessing

0

এটি ত্রুটি আপাতদৃষ্টিতে মিথ্যা-ধনাত্মক হওয়ার জন্য একটি কার্যকর প্রস্তাব দেয়। জেমস দ্বারা উল্লিখিত হিসাবে , এটি একটি ডেমনিক প্রক্রিয়া থেকে অনিচ্ছাকৃত আমদানির ক্ষেত্রে ঘটতে পারে ।

উদাহরণস্বরূপ, আপনার যদি নিম্নলিখিত সাধারণ কোডটি থাকে তবে WORKER_POOLঅসাবধানতাবশত কোনও শ্রমিকের কাছ থেকে আমদানি করা যায় যা ত্রুটির দিকে পরিচালিত করে।

import multiprocessing

WORKER_POOL = multiprocessing.Pool()

কার্যবিধির জন্য একটি সহজ তবে নির্ভরযোগ্য পন্থা হ'ল:

import multiprocessing
import multiprocessing.pool


class MyClass:

    @property
    def worker_pool(self) -> multiprocessing.pool.Pool:
        # Ref: https://stackoverflow.com/a/63984747/
        try:
            return self._worker_pool  # type: ignore
        except AttributeError:
            # pylint: disable=protected-access
            self.__class__._worker_pool = multiprocessing.Pool()  # type: ignore
            return self.__class__._worker_pool  # type: ignore
            # pylint: enable=protected-access

উপরের কাজটিতে MyClass.worker_poolত্রুটি ছাড়াই ব্যবহার করা যেতে পারে। আপনি যদি মনে করেন এই পদ্ধতির উন্নতি করা যায় তবে আমাকে জানান।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.