पाइथन मल्टीप्रोसेसिंग अचाररिंग: अचार <टाइप 'फंक्शन' नहीं कर सकते


243

मुझे खेद है कि मैं एक सरल उदाहरण के साथ त्रुटि को पुन: पेश नहीं कर सकता, और मेरा कोड पोस्ट करने के लिए बहुत जटिल है। अगर मैं नियमित अजगर के बजाय आईपीथॉन शेल में कार्यक्रम चलाता हूं, तो चीजें अच्छी तरह से काम करती हैं।

मैंने इस समस्या पर कुछ पिछले नोट देखे। वे सभी एक वर्ग फ़ंक्शन के भीतर परिभाषित फ़ंक्शन को कॉल करने के लिए पूल का उपयोग करने के कारण हुए थे। लेकिन मेरे लिए ऐसा नहीं है।

Exception in thread Thread-3:
Traceback (most recent call last):
  File "/usr/lib64/python2.7/threading.py", line 552, in __bootstrap_inner
    self.run()
  File "/usr/lib64/python2.7/threading.py", line 505, in run
    self.__target(*self.__args, **self.__kwargs)
  File "/usr/lib64/python2.7/multiprocessing/pool.py", line 313, in _handle_tasks
    put(task)
PicklingError: Can't pickle <type 'function'>: attribute lookup __builtin__.function failed

किसी भी सहायता के लिए धन्यवाद।

अद्यतन : फ़ंक्शन मैं अचार मॉड्यूल के शीर्ष स्तर पर परिभाषित किया गया है। हालांकि यह एक फ़ंक्शन को कॉल करता है जिसमें एक नेस्टेड फ़ंक्शन होता है। यानी, f()कॉल g()कॉल h()एक नेस्टेड समारोह है जो i(), और मैं बोल रहा हूँ pool.apply_async(f)f(), g(), h()सभी शीर्ष स्तर पर परिभाषित कर रहे हैं। मैंने इस पैटर्न के साथ सरल उदाहरण की कोशिश की और यह हालांकि काम करता है।


3
शीर्ष-स्तरीय / स्वीकृत उत्तर अच्छा है, लेकिन इसका मतलब हो सकता है कि आपको अपने कोड को पुन: संरचना करने की आवश्यकता हो, जो दर्दनाक हो सकता है। मैं किसी ऐसे व्यक्ति के लिए सिफारिश करूंगा, जिसके पास इस मुद्दे का उपयोग करने वाले अतिरिक्त उत्तरों को पढ़ने dillऔर pathos। हालाँकि, मैं vtkobjects के साथ काम करते समय किसी भी समाधान के साथ नहीं हूं :( किसी ने समानांतर प्रसंस्करण vtkPolyData में अजगर कोड चलाने में कामयाबी हासिल की है?
क्रिस

जवाबों:


305

यहाँ एक सूची दी जा सकती है कि क्या चुना जा सकता है । विशेष रूप से, फ़ंक्शन केवल पिकलेबल होते हैं यदि उन्हें किसी मॉड्यूल के शीर्ष-स्तर पर परिभाषित किया गया हो।

कोड का यह टुकड़ा:

import multiprocessing as mp

class Foo():
    @staticmethod
    def work(self):
        pass

if __name__ == '__main__':   
    pool = mp.Pool()
    foo = Foo()
    pool.apply_async(foo.work)
    pool.close()
    pool.join()

आपके द्वारा पोस्ट की गई एक त्रुटि लगभग समान है:

Exception in thread Thread-2:
Traceback (most recent call last):
  File "/usr/lib/python2.7/threading.py", line 552, in __bootstrap_inner
    self.run()
  File "/usr/lib/python2.7/threading.py", line 505, in run
    self.__target(*self.__args, **self.__kwargs)
  File "/usr/lib/python2.7/multiprocessing/pool.py", line 315, in _handle_tasks
    put(task)
PicklingError: Can't pickle <type 'function'>: attribute lookup __builtin__.function failed

समस्या यह है कि कार्यकर्ता प्रक्रियाओं को पास करने के लिए poolसभी तरीकों का उपयोग करते mp.SimpleQueueहैं। सब कुछ जो mp.SimpleQueueज़रूरी है, वह लेने योग्य होना चाहिए, और foo.workयह लेने योग्य नहीं है क्योंकि यह मॉड्यूल के शीर्ष स्तर पर परिभाषित नहीं है।

यह शीर्ष स्तर पर एक फ़ंक्शन को परिभाषित करके तय किया जा सकता है, जो कॉल करता है foo.work():

def work(foo):
    foo.work()

pool.apply_async(work,args=(foo,))

ध्यान देने योग्य fooहै कि Fooशीर्ष स्तर पर परिभाषित किया गया है, और foo.__dict__लेने योग्य है।


2
आपके जवाब के लिए धन्यवाद। मैंने अपना प्रश्न अपडेट किया। मुझे लगता है कि कारण, हालांकि htink नहीं है
वेंडेट्टा

7
एक PicklingError पाने के लिए कुछ ऐसा क्यू पर डालना होगा जो कि लाइक करने योग्य न हो। यह फ़ंक्शन या इसके तर्क हो सकते हैं। समस्या के बारे में और जानने के लिए, मैं सुझाव देता हूं कि अपने कार्यक्रम की एक प्रति बनाएँ, और इसे पार करना शुरू करें, इसे सरल और सरल बनाते हुए, हर बार यह देखने के लिए कार्यक्रम को फिर से चलाएं कि क्या समस्या बनी हुई है। जब यह वास्तव में सरल हो जाता है, तो आपको या तो स्वयं समस्या का पता चल जाएगा, या आपके पास कुछ ऐसा होगा जिसे आप यहां पोस्ट कर सकते हैं।
15

3
इसके अलावा: यदि आप किसी फ़ंक्शन को किसी मॉड्यूल के शीर्ष-स्तर पर परिभाषित करते हैं, लेकिन इसे सजाया गया है, तो संदर्भ डेकोरेटर के आउटपुट के लिए होगा, और आपको यह त्रुटि वैसे भी मिलेगी।
बॉबपोकेर्ट

5
केवल 5 साल की देरी से, लेकिन मैंने अभी इसमें भाग लिया है। यह पता चला है कि "शीर्ष स्तर" को सामान्य से अधिक शाब्दिक रूप से लिया जाना है: यह मुझे लगता है कि फ़ंक्शन परिभाषा को पूल के प्रारंभिककरण (यानी यहांpool = Pool() लाइन ) से पहले करना होगा। मुझे उम्मीद नहीं थी, और यही कारण हो सकता है कि ओपी की समस्या बनी रही।
एंड्रास डीक

4
विशेष रूप से, फ़ंक्शन केवल पिकलेबल होते हैं यदि उन्हें किसी मॉड्यूल के शीर्ष-स्तर पर परिभाषित किया गया हो। ऐसा प्रतीत होता है कि functool.partialशीर्ष-स्तरीय फ़ंक्शन पर लागू होने का परिणाम अचार-सक्षम भी है, भले ही इसे किसी अन्य फ़ंक्शन के अंदर परिभाषित किया गया हो।
user1071847

96

के pathos.multiprocesssingबजाय मैं का उपयोग करेंगे multiprocessing। उस उपयोग pathos.multiprocessingका एक कांटा है । अजगर में लगभग कुछ भी अनुक्रमित कर सकते हैं, इसलिए आप समानांतर में बहुत अधिक चारों ओर भेजने में सक्षम हैं। कांटा भी रूप में आप वर्ग तरीकों के लिए की जरूरत है, कई तर्क कार्यों के साथ सीधे काम करने की क्षमता है।multiprocessingdilldillpathos

>>> from pathos.multiprocessing import ProcessingPool as Pool
>>> p = Pool(4)
>>> class Test(object):
...   def plus(self, x, y): 
...     return x+y
... 
>>> t = Test()
>>> p.map(t.plus, x, y)
[4, 6, 8, 10]
>>> 
>>> class Foo(object):
...   @staticmethod
...   def work(self, x):
...     return x+1
... 
>>> f = Foo()
>>> p.apipe(f.work, f, 100)
<processing.pool.ApplyResult object at 0x10504f8d0>
>>> res = _
>>> res.get()
101

प्राप्त करें pathosऔर यदि आपको पसंद है, तो dillयहां: https://github.com/uqfoundation


5
इलाज किया। : किसी और के लिए, मैं दोनों पुस्तकालयों के माध्यम से स्थापित sudo pip install git+https://github.com/uqfoundation/dill.git@masterऔरsudo pip install git+https://github.com/uqfoundation/pathos.git@master
अलेक्जेंडर McFarlane

5
@AlexanderMcFarlane मैं sudoबाहरी पैकेजों से (विशेष रूप से गीथूब जैसे) से अजगर पैकेज स्थापित नहीं करूंगा । इसके बजाय, मैं चलाने की सिफारिश करूंगा:pip install --user git+...
क्रिस

सिर्फ प्रयोग pip install pathosकरने से दुःख नहीं होता है और यह संदेश देता है:Could not find a version that satisfies the requirement pp==1.5.7-pathos (from pathos)
xApple

11
pip install pathosअब काम करता है, और pathosअजगर 3 संगत है।
माइक मैक्वर्न्स

3
@DanielGoldfarb: कोड में कई स्थानों पर प्रतिस्थापित किया गया है multiprocess, multiprocessingजहां का एक कांटा है ... लेकिन अनिवार्य रूप से, यह बात है। कुछ अतिरिक्त API परतें प्रदान करता है और इसमें अतिरिक्त बैकएंड भी होते हैं। लेकिन, इसका सार यही है। dillpicklepathosmultiprocess
माइक मैककर्र्न

29

जैसा कि दूसरों ने कहा है कि multiprocessingकेवल पायथन ऑब्जेक्ट्स को कार्यकर्ता प्रक्रियाओं में स्थानांतरित कर सकते हैं जिन्हें चुना जा सकता है। यदि आप अपने कोड को अनटुब द्वारा वर्णित के रूप में पुनर्गठित नहीं कर सकते हैं, तो आप dillनीचे दिखाए गए अनुसार डेटा (विशेष रूप से कोड डेटा) को स्थानांतरित करने के लिए विस्तारित विस्तारित अचार / अनप्लगिंग क्षमताओं का उपयोग कर सकते हैं ।

इस समाधान के लिए केवल dillऔर किसी अन्य पुस्तकालय की स्थापना की आवश्यकता है pathos:

import os
from multiprocessing import Pool

import dill


def run_dill_encoded(payload):
    fun, args = dill.loads(payload)
    return fun(*args)


def apply_async(pool, fun, args):
    payload = dill.dumps((fun, args))
    return pool.apply_async(run_dill_encoded, (payload,))


if __name__ == "__main__":

    pool = Pool(processes=5)

    # asyn execution of lambda
    jobs = []
    for i in range(10):
        job = apply_async(pool, lambda a, b: (a, b, a * b), (i, i + 1))
        jobs.append(job)

    for job in jobs:
        print job.get()
    print

    # async execution of static method

    class O(object):

        @staticmethod
        def calc():
            return os.getpid()

    jobs = []
    for i in range(10):
        job = apply_async(pool, O.calc, ())
        jobs.append(job)

    for job in jobs:
        print job.get()

6
मैं लेखक dillऔर pathosलेखक हूं ... और जब आप सही होते हैं, तो क्या यह इतना अच्छा नहीं होता है कि pathosमेरे उत्तर में क्लीनर और अधिक लचीला हो ? या हो सकता है कि मैं थोड़ा पक्षपाती हूं ...
मैक मैकर्नस

4
मुझे pathosलेखन के समय की स्थिति के बारे में पता नहीं था और वह एक समाधान प्रस्तुत करना चाहता था जो उत्तर के बहुत निकट है। अब जब मैंने आपका समाधान देख लिया है तो मैं सहमत हूं कि यह रास्ता तय करना है।
रॉस्पोर्ट्रकर

मैंने तुम्हारा हल पढ़ा और जैसा था, वैसा ही Doh… I didn't even think of doing it like that. अच्छा था।
बजे माइक मैककर्न्स

4
पोस्टिंग के लिए धन्यवाद, मैंने इस दृष्टिकोण का उपयोग डिलिंग / अनिच्छुक तर्कों के लिए किया है जिन्हें चुना नहीं जा सकता है: stackoverflow.com/questions/27883574/…
jazzblue

@rocksportrocker। मैं इस उदाहरण को पढ़ रहा हूं और समझ नहीं पा रहा हूं कि स्पष्ट forलूप क्यों है । मैं सामान्य तौर पर देखूंगा कि समानांतर दिनचर्या एक सूची लेती है और बिना लूप के एक सूची वापस कर देती है।
user1700890

20

मैंने पाया है कि मैं इस पर प्रोफाइलर का उपयोग करने का प्रयास करके कोड के एक पूरी तरह से काम कर रहे टुकड़े पर वास्तव में उस त्रुटि आउटपुट को उत्पन्न कर सकता हूं।

ध्यान दें कि यह विंडोज पर था (जहां फोर्किंग थोड़ा कम सुरुचिपूर्ण है)।

मैं दौड़ रहा था:

python -m profile -o output.pstats <script> 

और पाया कि प्रोफाइलिंग को हटाने से त्रुटि दूर हो गई और प्रोफाइलिंग को फिर से स्थापित किया गया। मुझे बैटी ड्राइविंग कर रहा था क्योंकि मुझे पता था कि कोड काम करता था। मैं यह देखने के लिए जाँच कर रहा था कि क्या किसी ने पूलडोम अपडेट किया है ... तो डूबने का एहसास था और प्रोफाइलिंग को खत्म कर दिया और वह यह था।

किसी और के मामले में अभिलेखागार के लिए यहां पोस्ट करना।


3
वाह, उल्लेख करने के लिए धन्यवाद! इसने मुझे आखिरी घंटे के लिए पागल कर दिया; मैंने बहुत सरल उदाहरण तक सब कुछ करने की कोशिश की - कुछ भी काम नहीं लगा। लेकिन मैं भी अपने बैचफाइल के माध्यम से चलने वाले प्रोफाइलर था :(
टिम

1
ओह, आपको पर्याप्त धन्यवाद नहीं दे सकता। यह इतना मूर्खतापूर्ण है, हालांकि यह बहुत अप्रत्याशित है। मुझे लगता है कि इसका उल्लेख डॉक्स में किया जाना चाहिए। सब मैं एक आयात पीडीबी बयान था, और सिर्फ एक passअचार के साथ एक साधारण शीर्ष स्तर का कार्य नहीं था।
0xc0de

10

जब यह समस्या multiprocessingएक सरल समाधान के साथ आती है तो इससे स्विच करना होता Poolहै ThreadPool। यह आयात के अलावा किसी अन्य कोड के परिवर्तन के साथ नहीं किया जा सकता है-

from multiprocessing.pool import ThreadPool as Pool

यह काम करता है क्योंकि थ्रेडपूल एक नई प्रक्रिया बनाने के बजाय मुख्य थ्रेड के साथ मेमोरी साझा करता है- इसका मतलब है कि अचार की आवश्यकता नहीं है।

इस पद्धति का नकारात्मक पक्ष यह है कि थ्रेड को संभालने के साथ अजगर सबसे बड़ी भाषा नहीं है- यह थ्रेड को सुरक्षित रखने के लिए ग्लोबल इंटरप्रेटर लॉक नामक कुछ का उपयोग करता है, जो यहां कुछ उपयोग मामलों को धीमा कर सकता है। हालाँकि, यदि आप मुख्य रूप से अन्य सिस्टम के साथ बातचीत कर रहे हैं (HTTP कमांड्स चला रहे हैं, डेटाबेस के साथ बात कर रहे हैं, फाइल सिस्टम पर लिख रहे हैं) तो आपका कोड सीपीयू द्वारा बाध्य नहीं है और बहुत हिट नहीं होगा। वास्तव में मैंने HTTP / HTTPS बेंचमार्क लिखते समय पाया है कि यहां उपयोग किए गए थ्रेडेड मॉडल में ओवरहेड और देरी होती है, क्योंकि नई प्रक्रियाओं को बनाने के लिए ओवरहेड नए धागे बनाने के लिए ओवरहेड की तुलना में बहुत अधिक है।

तो अगर आप अजगर उपयोगकर्ताओं की एक टन सामग्री का प्रसंस्करण कर रहे हैं तो यह सबसे अच्छा तरीका नहीं हो सकता है।


2
लेकिन तब आप केवल एक सीपीयू (कम से कम नियमित पायथन संस्करणों के साथ जो कि जीआईएल का उपयोग करते हैं ) का उपयोग कर रहे हैं , जो उद्देश्य को हरा देता है।
एंड्रे दोनों

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

यह सच है, धन्यवाद। फिर भी आप उत्तर में एक चेतावनी शामिल करना चाह सकते हैं। इन दिनों जब प्रसंस्करण शक्ति बढ़ती है तो अधिक शक्तिशाली सीपीयू कोर के बजाय अधिक के रूप में आते हैं, मल्टीकोर से सिंगल-कोर निष्पादन पर स्विच करना एक महत्वपूर्ण साइड इफेक्ट है।
एंड्रे दोनों

अच्छा बिंदु- मैंने अधिक विवरण के साथ उत्तर को अपडेट किया है। मैं यह बताना चाहता हूं कि थ्रेडेड मल्टीप्रोसेसिंग पर स्विच करने से अजगर केवल एक कोर पर कार्य नहीं करता है।
टेडिवम

4

इस समाधान में केवल डिल की स्थापना की आवश्यकता है और पथ के रूप में अन्य पुस्तकालयों की आवश्यकता नहीं है

def apply_packed_function_for_map((dumped_function, item, args, kwargs),):
    """
    Unpack dumped function as target function and call it with arguments.

    :param (dumped_function, item, args, kwargs):
        a tuple of dumped function and its arguments
    :return:
        result of target function
    """
    target_function = dill.loads(dumped_function)
    res = target_function(item, *args, **kwargs)
    return res


def pack_function_for_map(target_function, items, *args, **kwargs):
    """
    Pack function and arguments to object that can be sent from one
    multiprocessing.Process to another. The main problem is:
        «multiprocessing.Pool.map*» or «apply*»
        cannot use class methods or closures.
    It solves this problem with «dill».
    It works with target function as argument, dumps it («with dill»)
    and returns dumped function with arguments of target function.
    For more performance we dump only target function itself
    and don't dump its arguments.
    How to use (pseudo-code):

        ~>>> import multiprocessing
        ~>>> images = [...]
        ~>>> pool = multiprocessing.Pool(100500)
        ~>>> features = pool.map(
        ~...     *pack_function_for_map(
        ~...         super(Extractor, self).extract_features,
        ~...         images,
        ~...         type='png'
        ~...         **options,
        ~...     )
        ~... )
        ~>>>

    :param target_function:
        function, that you want to execute like  target_function(item, *args, **kwargs).
    :param items:
        list of items for map
    :param args:
        positional arguments for target_function(item, *args, **kwargs)
    :param kwargs:
        named arguments for target_function(item, *args, **kwargs)
    :return: tuple(function_wrapper, dumped_items)
        It returs a tuple with
            * function wrapper, that unpack and call target function;
            * list of packed target function and its' arguments.
    """
    dumped_function = dill.dumps(target_function)
    dumped_items = [(dumped_function, item, args, kwargs) for item in items]
    return apply_packed_function_for_map, dumped_items

यह सुन्न सरणियों के लिए भी काम करता है।


2
Can't pickle <type 'function'>: attribute lookup __builtin__.function failed

यह त्रुटि भी आएगी यदि आपके पास उस मॉडल ऑब्जेक्ट के अंदर कोई इनबिल्ट फ़ंक्शन है जो async जॉब को दिया गया था।

तो सुनिश्चित करें कि जो मॉडल ऑब्जेक्ट पास किए गए हैं उनमें इनबिल्ट फ़ंक्शन नहीं हैं। (हमारे मामले में हम एक निश्चित क्षेत्र को ट्रैक करने के लिए मॉडल के अंदर django-model-utils केFieldTracker() फ़ंक्शन का उपयोग कर रहे थे )। यहाँ प्रासंगिक GitHub मुद्दे की कड़ी है।


0

@Rocksportrocker समाधान पर निर्माण, यह भेजने और RECVing परिणामों को रिकॉर्ड करने के दौरान डिल करने के लिए समझ में आता है।

import dill
import itertools
def run_dill_encoded(payload):
    fun, args = dill.loads(payload)
    res = fun(*args)
    res = dill.dumps(res)
    return res

def dill_map_async(pool, fun, args_list,
                   as_tuple=True,
                   **kw):
    if as_tuple:
        args_list = ((x,) for x in args_list)

    it = itertools.izip(
        itertools.cycle([fun]),
        args_list)
    it = itertools.imap(dill.dumps, it)
    return pool.map_async(run_dill_encoded, it, **kw)

if __name__ == '__main__':
    import multiprocessing as mp
    import sys,os
    p = mp.Pool(4)
    res = dill_map_async(p, lambda x:[sys.stdout.write('%s\n'%os.getpid()),x][-1],
                  [lambda x:x+1]*10,)
    res = res.get(timeout=100)
    res = map(dill.loads,res)
    print(res)
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.