मैं मल्टीप्रोसेसिंग के लिए दिए गए फ़ंक्शन के रिटर्न मान को कैसे पुनर्प्राप्त कर सकता हूं।प्रोसेस?


190

नीचे दिए गए उदाहरण कोड में, मैं फ़ंक्शन के रिटर्न मान को पुनर्प्राप्त करना चाहता हूं worker। मैं ऐसा कैसे कर सकता हूं? यह मान कहाँ संग्रहीत है?

उदाहरण कोड:

import multiprocessing

def worker(procnum):
    '''worker function'''
    print str(procnum) + ' represent!'
    return procnum


if __name__ == '__main__':
    jobs = []
    for i in range(5):
        p = multiprocessing.Process(target=worker, args=(i,))
        jobs.append(p)
        p.start()

    for proc in jobs:
        proc.join()
    print jobs

आउटपुट:

0 represent!
1 represent!
2 represent!
3 represent!
4 represent!
[<Process(Process-1, stopped)>, <Process(Process-2, stopped)>, <Process(Process-3, stopped)>, <Process(Process-4, stopped)>, <Process(Process-5, stopped)>]

में संग्रहीत वस्तुओं में मैं प्रासंगिक विशेषता खोजने के लिए प्रतीत नहीं कर सकता jobs

जवाबों:


189

संवाद करने के लिए साझा चर का उपयोग करें । इस तरह के उदाहरण के लिए:

import multiprocessing

def worker(procnum, return_dict):
    '''worker function'''
    print str(procnum) + ' represent!'
    return_dict[procnum] = procnum


if __name__ == '__main__':
    manager = multiprocessing.Manager()
    return_dict = manager.dict()
    jobs = []
    for i in range(5):
        p = multiprocessing.Process(target=worker, args=(i,return_dict))
        jobs.append(p)
        p.start()

    for proc in jobs:
        proc.join()
    print return_dict.values()

46
मैं यहाँ के multiprocessing.Queueबजाय एक का उपयोग करने की सलाह दूंगा Manager। एक का उपयोग करते हुए Managerएक पूरी तरह से नई प्रक्रिया है, जो overkill है जब एक को उत्पन्न करने की आवश्यकता है Queueकरना होगा।
डैनो

1
@ डानो: मुझे आश्चर्य है, अगर हम क्यू () ऑब्जेक्ट का उपयोग करते हैं, तो हम यह सुनिश्चित नहीं कर सकते हैं कि जब प्रत्येक प्रक्रिया मान लौटाएगी। मेरा मतलब है कि अगर हमें अगले काम करने के लिए परिणाम में आदेश की आवश्यकता है। हम यह कैसे सुनिश्चित कर सकते हैं कि वास्तव में कौन सा आउटपुट किस प्रक्रिया से है
कैटबाइट्स सिप

4
@ कैटिगरीब आप प्रत्येक प्रक्रिया से एक टपल लौटा सकते हैं, जहां एक मान वास्तविक रिटर्न मूल्य है जिसकी आप परवाह करते हैं, और दूसरा प्रक्रिया से एक विशिष्ट पहचानकर्ता है। लेकिन मुझे यह भी आश्चर्य है कि आपको यह जानने की आवश्यकता है कि कौन सी प्रक्रिया किस मूल्य पर लौट रही है। यदि आपको वास्तव में इस प्रक्रिया के बारे में जानने की आवश्यकता है, या क्या आपको इनपुट की अपनी सूची और आउटपुट की सूची के बीच सहसंबंध बनाने की आवश्यकता है? उस स्थिति में, मैं multiprocessing.Pool.mapआपके कार्य आइटम की सूची को संसाधित करने के लिए उपयोग करने की सलाह दूंगा।
दानो

4
केवल एक तर्क के साथ कार्यों के लिए चेतावनी : उपयोग करना चाहिए args=(my_function_argument, ),यहाँ अल्पविराम पर ध्यान दें ! या फिर पायथन "लापता स्थिति संबंधी दलीलों" की शिकायत करेगा। मुझे पता लगाने के लिए 10 मिनट लग गए। यह भी जांच के मैनुअल उपयोग ( "प्रक्रिया वर्ग" के तहत)।
युक्ली

2
@vartec एक मल्टीप्रोसेसिंग का उपयोग करने का एक दोष है। मानेगर () शब्दकोश यह है कि अचार है (क्रमबद्ध) वह जिस वस्तु को लौटाता है, इसलिए उसमें ऑब्जेक्ट के लौटने के लिए अधिकतम 2GiB आकार के अचार पुस्तकालय द्वारा दी गई अड़चन होती है। क्या ऐसा करने का कोई अन्य तरीका है जो रिटर्निंग ऑब्जेक्ट के क्रमांकन से बचता है?
hirschme

67

मुझे लगता है कि @sega_sai द्वारा सुझाया गया दृष्टिकोण बेहतर है। लेकिन यह वास्तव में एक कोड उदाहरण की जरूरत है, तो यहाँ जाता है:

import multiprocessing
from os import getpid

def worker(procnum):
    print('I am number %d in process %d' % (procnum, getpid()))
    return getpid()

if __name__ == '__main__':
    pool = multiprocessing.Pool(processes = 3)
    print(pool.map(worker, range(5)))

जो रिटर्न वैल्यू प्रिंट करेगा:

I am number 0 in process 19139
I am number 1 in process 19138
I am number 2 in process 19140
I am number 3 in process 19139
I am number 4 in process 19140
[19139, 19138, 19140, 19139, 19140]

यदि आप map(पायथन 2 बिल्ट-इन) से परिचित हैं तो यह बहुत चुनौतीपूर्ण नहीं होना चाहिए। अन्यथा sega_Sai के लिंक पर एक नज़र डालें ।

ध्यान दें कि कितने कोड की आवश्यकता है। (यह भी ध्यान दें कि प्रक्रियाओं का पुन: उपयोग कैसे किया जाता है)।


1
किसी भी विचार क्यों मेरे getpid()सभी एक ही मूल्य वापस? मैं Python3 चला रहा हूँ
zelusp

मुझे यकीन नहीं है कि पूल श्रमिकों पर कार्य कैसे वितरित करता है। शायद वे सभी एक ही कार्यकर्ता पर समाप्त हो सकते हैं यदि वे वास्तव में तेज़ हैं? क्या यह लगातार होता है? इसके अलावा अगर आप देरी जोड़ते हैं?
मार्क

मैंने यह भी सोचा था कि यह एक गति से संबंधित बात थी लेकिन जब मैं pool.map10 से अधिक प्रक्रियाओं का उपयोग करके 1,000,000 की एक श्रृंखला खिलाता हूं तो मुझे सबसे अधिक दो अलग-अलग चरणों में दिखाई देता है।
zelusp

1
तब मुझे यकीन नहीं हुआ। मुझे लगता है कि इसके लिए एक अलग प्रश्न खोलना दिलचस्प होगा।
मार्क

यदि आप प्रत्येक प्रक्रिया को एक अलग फ़ंक्शन भेजना चाहते हैं, तो उपयोग करें pool.apply_async: docs.python.org/3/library/…
Kyle

24

यह उदाहरण दिखाता है कि मल्टीप्रोसेसिंग की सूची का उपयोग कैसे करें । प्रक्रियाओं की एक मनमानी संख्या से स्ट्रिंग को वापस करने के लिए उदाहरणों को मिटाएं :

import multiprocessing

def worker(procnum, send_end):
    '''worker function'''
    result = str(procnum) + ' represent!'
    print result
    send_end.send(result)

def main():
    jobs = []
    pipe_list = []
    for i in range(5):
        recv_end, send_end = multiprocessing.Pipe(False)
        p = multiprocessing.Process(target=worker, args=(i, send_end))
        jobs.append(p)
        pipe_list.append(recv_end)
        p.start()

    for proc in jobs:
        proc.join()
    result_list = [x.recv() for x in pipe_list]
    print result_list

if __name__ == '__main__':
    main()

आउटपुट:

0 represent!
1 represent!
2 represent!
3 represent!
4 represent!
['0 represent!', '1 represent!', '2 represent!', '3 represent!', '4 represent!']

यह समाधान मल्टीप्रोसेसिंग से कम संसाधनों का उपयोग करता है। क्यू का उपयोग करता है

  • एक पाइप
  • कम से कम एक ताला
  • एक बफर
  • एक धागा

या मल्टीप्रोसेसिंग। सिम्प्लीक्यूयू का उपयोग करता है

  • एक पाइप
  • कम से कम एक ताला

इन प्रकारों में से प्रत्येक के लिए स्रोत को देखना बहुत ही शिक्षाप्रद है।


ऐसा करने का सबसे अच्छा तरीका क्या होगा कि पाइप को एक वैश्विक चर बनाने के बिना?
निकिक

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

क्या पाइप को हमेशा किसी नए मूल्य में जोड़ने (भेजे जाने) से पहले पढ़ा जाना चाहिए?
निकपिक

+1, अच्छा जवाब। लेकिन समाधान अधिक कुशल होने के बारे में, ट्रेडऑफ़ यह है कि आप सभी प्रक्रियाओं के लिए एक Pipeप्रति प्रक्रिया बनाम एक बना रहे Queueहैं। मुझे नहीं पता कि यह सभी मामलों में अधिक कुशल है।
sudo

2
यह उत्तर एक गतिरोध का कारण बनता है यदि रिटर्निंग ऑब्जेक्ट बड़ा है। Proc.join () करने के बजाय, मैं सबसे पहले रिटर्न वैल्यू को पुनः प्राप्त () करने की कोशिश करूंगा और फिर जॉइन करूंगा।
एल। पैसन

21

किसी कारण से, मुझे यह देखने का एक सामान्य उदाहरण नहीं मिला कि इसे Queueकहीं भी कैसे किया जाए (यहां तक ​​कि पायथन के डॉक उदाहरण भी कई प्रक्रियाओं को स्पॉन नहीं करते हैं), इसलिए यहां 10 कोशिशों के बाद मुझे क्या काम मिला:

def add_helper(queue, arg1, arg2): # the func called in child processes
    ret = arg1 + arg2
    queue.put(ret)

def multi_add(): # spawns child processes
    q = Queue()
    processes = []
    rets = []
    for _ in range(0, 100):
        p = Process(target=add_helper, args=(q, 1, 2))
        processes.append(p)
        p.start()
    for p in processes:
        ret = q.get() # will block
        rets.append(ret)
    for p in processes:
        p.join()
    return rets

Queueएक ब्लॉकिंग, थ्रेड-सेफ कतार है जिसका उपयोग आप चाइल्ड प्रोसेस से रिटर्न वैल्यू को स्टोर करने के लिए कर सकते हैं। इसलिए आपको प्रत्येक प्रक्रिया के लिए कतार से गुजरना होगा। यहाँ कुछ कम स्पष्ट है कि आपको get()कतार से पहले joinही Processes है वरना कतार भर जाती है और सब कुछ ब्लॉक कर देती है।

ऑब्जेक्ट-ओरिएंटेड (पायथन 3.4 में परीक्षण) के लिए अपडेट करें :

from multiprocessing import Process, Queue

class Multiprocessor():

    def __init__(self):
        self.processes = []
        self.queue = Queue()

    @staticmethod
    def _wrapper(func, queue, args, kwargs):
        ret = func(*args, **kwargs)
        queue.put(ret)

    def run(self, func, *args, **kwargs):
        args2 = [func, self.queue, args, kwargs]
        p = Process(target=self._wrapper, args=args2)
        self.processes.append(p)
        p.start()

    def wait(self):
        rets = []
        for p in self.processes:
            ret = self.queue.get()
            rets.append(ret)
        for p in self.processes:
            p.join()
        return rets

# tester
if __name__ == "__main__":
    mp = Multiprocessor()
    num_proc = 64
    for _ in range(num_proc): # queue up multiple tasks running `sum`
        mp.run(sum, [1, 2, 3, 4, 5])
    ret = mp.wait() # get all results
    print(ret)
    assert len(ret) == num_proc and all(r == 15 for r in ret)

18

किसी और के लिए जो एक Processउपयोग से मूल्य प्राप्त करना चाहता है Queue:

import multiprocessing

ret = {'foo': False}

def worker(queue):
    ret = queue.get()
    ret['foo'] = True
    queue.put(ret)

if __name__ == '__main__':
    queue = multiprocessing.Queue()
    queue.put(ret)
    p = multiprocessing.Process(target=worker, args=(queue,))
    p.start()
    print queue.get()  # Prints {"foo": True}
    p.join()

1
जब मैं अपने कार्यकर्ता प्रक्रिया में एक कतार में कुछ डालता हूं तो मेरा जुड़ाव कभी नहीं होता है। किसी भी विचार यह कैसे आ सकता है?
लॉरेन्स कोपेनोल

@LaurensKoppenol का मतलब है कि आपका मुख्य कोड p.join () पर स्थायी रूप से लटका रहता है और कभी जारी नहीं रहता है? क्या आपकी प्रक्रिया में एक अनंत लूप है?
मैथ्यू मोइसन

4
हां, यह वहां लटका हुआ है। मेरे कार्यकर्ता सभी खत्म कर देते हैं (कार्यकर्ता कार्य के भीतर लूप, प्रिंट स्टेटमेंट बाद में मुद्रित होता है, सभी श्रमिकों के लिए)। ज्वाइन कुछ नहीं करता है। अगर मैं Queueअपने फंक्शन से हटा दूं तो यह मुझे पास कर देगाjoin()
लॉरेंस कोपेनोल

@LaurensKoppenol क्या आप शायद कॉल करने queue.put(ret)से पहले कॉल नहीं कर रहे हैं p.start()? उस स्थिति में, श्रमिक धागा queue.get()हमेशा के लिए लटक जाएगा। आप बाहर टिप्पणी करते समय ऊपर मेरे स्निपेट को कॉपी करके इसे दोहरा सकते हैं queue.put(ret)
मैथ्यू मोइसन

मैंने इस उत्तर को संपादित किया है, के queue.get()पहले होना था p.join()। यह अब मेरे लिए काम करता है।
jfunk

12

ऐसा लगता है कि आपको इसके बजाय मल्टीप्रोसेसिंग .पूल क्लास का उपयोग करना चाहिए और तरीकों का उपयोग करना चाहिए .apply () .apply_async (), मैप ()

http://docs.python.org/library/multiprocessing.html?highlight=pool#multiprocessing.pool.AsyncResult


मेरे पास टेंसरफ़्लो कोड है, जिसके लिए मल्टीप्रोसेसिंग है।पूल लटका होगा, लेकिन मल्टीप्रोसेसिंग नहीं। प्रोसेस
ले फ्राइट

10

आप exitकिसी प्रोसेस के एक्जिट कोड को सेट करने के लिए बिल्ट-इन का उपयोग कर सकते हैं । इसे exitcodeप्रक्रिया की विशेषता से प्राप्त किया जा सकता है:

import multiprocessing

def worker(procnum):
    print str(procnum) + ' represent!'
    exit(procnum)

if __name__ == '__main__':
    jobs = []
    for i in range(5):
        p = multiprocessing.Process(target=worker, args=(i,))
        jobs.append(p)
        p.start()

    result = []
    for proc in jobs:
        proc.join()
        result.append(proc.exitcode)
    print result

आउटपुट:

0 represent!
1 represent!
2 represent!
3 represent!
4 represent!
[0, 1, 2, 3, 4]

4
चेतावनी दी है कि यह दृष्टिकोण भ्रामक हो सकता है। प्रक्रियाओं को आम तौर पर निकास कोड के साथ बाहर निकलना चाहिए 0 वे त्रुटि के बिना पूरा हो गया है। यदि आपके पास अपने सिस्टम प्रोसेस एग्जिट कोड्स की निगरानी करने के लिए कुछ भी है तो आप इन रिपोर्ट को त्रुटियों के रूप में देख सकते हैं।
फेरूशेवेल

1
बिल्कुल सही अगर आप त्रुटि पर मूल प्रक्रिया में अपवाद उठाना चाहते हैं।
crizCraig


3

सोचा था कि मैं ऊपर से कॉपी किए गए सरल उदाहरणों को सरल बनाऊंगा, मेरे लिए Py3.6 पर काम कर रहा है। सबसे सरल है multiprocessing.Pool:

import multiprocessing
import time

def worker(x):
    time.sleep(1)
    return x

pool = multiprocessing.Pool()
print(pool.map(worker, range(10)))

आप उदाहरण के लिए, पूल में प्रक्रियाओं की संख्या निर्धारित कर सकते हैं Pool(processes=5)। हालांकि यह सीपीयू की गिनती में चूक करता है, इसलिए इसे सीपीयू-बाउंड कार्यों के लिए खाली छोड़ दें। (I / O- बाउंड कार्य अक्सर थ्रेड्स को सूट करते हैं, क्योंकि थ्रेड्स अधिकतर प्रतीक्षा कर रहे हैं इसलिए CPU कोर साझा कर सकते हैं।) चूनिंग ऑप्टिमाइज़ेशनPool भी लागू होता है

(ध्यान दें कि कार्यकर्ता विधि को किसी विधि के भीतर नहीं किया जा सकता है। मैंने शुरू में मेरे कार्यकर्ता पद्धति को उस पद्धति के अंदर परिभाषित किया था जो कॉल करने के लिए pool.map, इसे सभी आत्म-निहित रखने के लिए है, लेकिन फिर प्रक्रियाएं इसे आयात नहीं कर सकती हैं, और फेंक दिया "AttributeError : स्थानीय वस्तु को बाहर नहीं ले जा सकते बाहरी_method..inner_method "। यहाँ और अधिक । यह एक वर्ग के अंदर हो सकता है। "

( 'represent!'इसके बजाय मूल प्रश्न निर्दिष्ट मुद्रण की सराहना करें time.sleep(), लेकिन इसके बिना मुझे लगा कि कुछ कोड समवर्ती रूप से चल रहा था जब यह नहीं था।)


Py3 की ProcessPoolExecutorदो पंक्तियाँ भी हैं ( .mapजनरेटर की आवश्यकता होती है list())

from concurrent.futures import ProcessPoolExecutor
with ProcessPoolExecutor() as executor:
    print(list(executor.map(worker, range(10))))

सादे Processतों के साथ :

import multiprocessing
import time

def worker(x, queue):
    time.sleep(1)
    queue.put(x)

queue = multiprocessing.SimpleQueue()
tasks = range(10)

for task in tasks:
    multiprocessing.Process(target=worker, args=(task, queue,)).start()

for _ in tasks:
    print(queue.get())

SimpleQueueयदि आपकी जरूरत है putऔर उपयोग करें get। पहला लूप सभी प्रक्रियाओं को शुरू करता है, इससे पहले कि दूसरा अवरुद्ध queue.getकॉल करता है। मुझे नहीं लगता कि कॉल करने का कोई कारण p.join()है।


2

एक सरल उपाय:

import multiprocessing

output=[]
data = range(0,10)

def f(x):
    return x**2

def handler():
    p = multiprocessing.Pool(64)
    r=p.map(f, data)
    return r

if __name__ == '__main__':
    output.append(handler())

print(output[0])

आउटपुट:

[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

2

यदि आप पायथन 3 का उपयोग कर रहे हैं, तो आप concurrent.futures.ProcessPoolExecutorएक सुविधाजनक अमूर्त के रूप में उपयोग कर सकते हैं :

from concurrent.futures import ProcessPoolExecutor

def worker(procnum):
    '''worker function'''
    print(str(procnum) + ' represent!')
    return procnum


if __name__ == '__main__':
    with ProcessPoolExecutor() as executor:
        print(list(executor.map(worker, range(5))))

आउटपुट:

0 represent!
1 represent!
2 represent!
3 represent!
4 represent!
[0, 1, 2, 3, 4]

0

मैंने फ़ंक्शन से त्रुटि कोड प्राप्त करने के लिए आवश्यक vartec के उत्तर को थोड़ा संशोधित किया। (साभार अनुकृति !!! इसकी जबरदस्त ट्रिक)

यह भी किया जा सकता है, manager.listलेकिन मुझे लगता है कि यह एक तानाशाही में बेहतर है और इसके भीतर एक सूची संग्रहीत करें। इस तरह, जिस तरह से हम फ़ंक्शन और परिणाम रखते हैं, क्योंकि हम उस क्रम के बारे में सुनिश्चित नहीं हो सकते हैं जिसमें सूची पॉपुलेटेड होगी।

from multiprocessing import Process
import time
import datetime
import multiprocessing


def func1(fn, m_list):
    print 'func1: starting'
    time.sleep(1)
    m_list[fn] = "this is the first function"
    print 'func1: finishing'
    # return "func1"  # no need for return since Multiprocess doesnt return it =(

def func2(fn, m_list):
    print 'func2: starting'
    time.sleep(3)
    m_list[fn] = "this is function 2"
    print 'func2: finishing'
    # return "func2"

def func3(fn, m_list):
    print 'func3: starting'
    time.sleep(9)
    # if fail wont join the rest because it never populate the dict
    # or do a try/except to get something in return.
    raise ValueError("failed here")
    # if we want to get the error in the manager dict we can catch the error
    try:
        raise ValueError("failed here")
        m_list[fn] = "this is third"
    except:
        m_list[fn] = "this is third and it fail horrible"
        # print 'func3: finishing'
        # return "func3"


def runInParallel(*fns):  # * is to accept any input in list
    start_time = datetime.datetime.now()
    proc = []
    manager = multiprocessing.Manager()
    m_list = manager.dict()
    for fn in fns:
        # print fn
        # print dir(fn)
        p = Process(target=fn, name=fn.func_name, args=(fn, m_list))
        p.start()
        proc.append(p)
    for p in proc:
        p.join()  # 5 is the time out

    print datetime.datetime.now() - start_time
    return m_list, proc

if __name__ == '__main__':
    manager, proc = runInParallel(func1, func2, func3)
    # print dir(proc[0])
    # print proc[0]._name
    # print proc[0].name
    # print proc[0].exitcode

    # here you can check what did fail
    for i in proc:
        print i.name, i.exitcode  # name was set up in the Process line 53

    # here will only show the function that worked and where able to populate the 
    # manager dict
    for i, j in manager.items():
        print dir(i)  # things you can do to the function
        print i, j
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.