थ्रेडिंग और मल्टीप्रोसेसिंग मॉड्यूल के बीच अंतर क्या हैं?


141

मैं सीख रहा हूँ कि कैसे पायथन threadingऔर multiprocessingमॉड्यूल का उपयोग करके समानांतर में कुछ ऑपरेशन चलाएं और मेरे कोड को गति दें।

मुझे यह मुश्किल लग रहा है (हो सकता है क्योंकि मेरे पास इसके बारे में कोई सैद्धांतिक पृष्ठभूमि नहीं है) यह समझने के लिए कि एक threading.Thread()वस्तु और एक के बीच क्या अंतर है multiprocessing.Process()

इसके अलावा, यह मेरे लिए पूरी तरह से स्पष्ट नहीं है कि नौकरियों की एक कतार को तुरंत कैसे लागू किया जाए और उनमें से केवल 4 (उदाहरण के लिए) समानांतर में चल रहे हैं, जबकि अन्य निष्पादित होने से पहले संसाधनों के मुक्त होने की प्रतीक्षा करते हैं।

मुझे दस्तावेज़ीकरण में उदाहरण स्पष्ट हैं, लेकिन बहुत विस्तृत नहीं; जैसे ही मैं चीजों को थोड़ा जटिल करने की कोशिश करता हूं, मुझे बहुत सारी अजीब त्रुटियां प्राप्त होती हैं (जैसे कि एक विधि जिसे चुना नहीं जा सकता, और इसी तरह)।

तो, मुझे कब threadingऔर multiprocessingमॉड्यूल का उपयोग करना चाहिए ?

क्या आप मुझे कुछ संसाधनों से जोड़ सकते हैं जो इन दो मॉड्यूलों के पीछे की अवधारणाओं को समझाते हैं और उन्हें जटिल कार्यों के लिए ठीक से उपयोग कैसे करें?


वहाँ अधिक है, वहाँ भी Threadमॉड्यूल ( _threadअजगर 3.x में कहा जाता है)। सच कहूं तो, मैंने खुद मतभेदों को कभी नहीं समझा है ...
डनो

3
@ डननो: जैसा कि Thread/ _threadप्रलेखन स्पष्ट रूप से कहता है, यह "निम्न-स्तरीय प्राइमेटीस" है। आप इसका उपयोग कस्टम सिंक्रोनाइज़ेशन ऑब्जेक्ट बनाने में कर सकते हैं, धागे के एक पेड़ के शामिल होने के क्रम को नियंत्रित करने के लिए, आदि। यदि आप कल्पना नहीं कर सकते कि आपको इसका उपयोग करने की आवश्यकता क्यों है, तो इसका उपयोग न करें, और इसके साथ रहें threading
अगस्ता

जवाबों:


260

Giulio फ्रेंको का कहना है कि मल्टीथ्रेडिंग बनाम मल्टीप्रोसेसिंग के लिए सामान्य रूप से सच है ।

हालाँकि, पायथन * में एक और समस्या है: एक ग्लोबल इंटरप्रेटर लॉक है जो एक ही समय में एक ही प्रक्रिया में दो थ्रेड्स को पायथन कोड को चलाने से रोकता है। इसका मतलब है कि अगर आपके पास 8 कोर हैं, और 8 थ्रेड्स का उपयोग करने के लिए अपने कोड को बदल दें, तो यह 800% सीपीयू का उपयोग करने और 8x तेज़ी से चलाने में सक्षम नहीं होगा; यह समान 100% CPU का उपयोग करेगा और समान गति से चलेगा। (वास्तव में, यह थोड़ा धीमा चलेगा, क्योंकि थ्रेडिंग से अतिरिक्त ओवरहेड है, भले ही आपके पास कोई साझा डेटा नहीं है, लेकिन अभी के लिए इसे अनदेखा करें।)

इसके अपवाद हैं। यदि आपके कोड की भारी गणना वास्तव में पायथन में नहीं होती है, लेकिन कुछ CIL के साथ कस्टम C कोड के साथ कुछ लाइब्रेरी में, जो कि एक खस्ता ऐप की तरह उचित GIL हैंडलिंग करता है, तो आपको थ्रेडिंग से अपेक्षित प्रदर्शन लाभ मिलेगा। अगर यह सच है कि भारी गणना कुछ सबप्रोसेस द्वारा की जाती है जो आप चलाते हैं और प्रतीक्षा करते हैं।

इससे भी महत्वपूर्ण बात यह है कि ऐसे मामले हैं जहां यह कोई मायने नहीं रखता है। उदाहरण के लिए, एक नेटवर्क सर्वर अपना अधिकांश समय नेटवर्क से पैकेट पढ़ने में बिताता है, और GUI ऐप अपना अधिकांश समय उपयोगकर्ता की घटनाओं के इंतजार में बिताता है। नेटवर्क सर्वर या GUI ऐप में थ्रेड्स का उपयोग करने का एक कारण यह है कि आप लंबे समय तक चलने वाले "बैकग्राउंड टास्क" को बिना मेन नेटवर्क स्टॉप या GUI इवेंट्स को जारी रखने से रोक सकते हैं। और वह पायथन धागे के साथ ठीक काम करता है। (तकनीकी शब्दों में, इसका मतलब है कि पायथन थ्रेड्स आपको समसामयिकता प्रदान करते हैं, भले ही वे आपको कोर-समानांतरता न दें।)

लेकिन यदि आप शुद्ध पायथन में सीपीयू-बाउंड प्रोग्राम लिख रहे हैं, तो अधिक थ्रेड का उपयोग करना आमतौर पर उपयोगी नहीं होता है।

अलग-अलग प्रक्रियाओं का उपयोग करने से GIL को ऐसी कोई समस्या नहीं है, क्योंकि प्रत्येक प्रक्रिया की अपनी अलग GIL है। बेशक, आपके पास अभी भी थ्रेड्स और प्रक्रियाओं के बीच सभी समान ट्रेडऑफ़ हैं जैसा कि अन्य भाषाओं में है - थ्रेड्स की तुलना में प्रक्रियाओं के बीच डेटा साझा करना अधिक कठिन और अधिक महंगा है, बड़ी संख्या में प्रक्रियाओं को चलाना या बनाना और नष्ट करना महंगा हो सकता है। उन्हें अक्सर, आदि लेकिन जीआईएल प्रक्रियाओं की ओर संतुलन पर भारी होता है, इस तरह से, सी या जावा के लिए सही नहीं है। इसलिए, आप अपने आप को सी या जावा की तुलना में पायथन में अधिक बार मल्टीप्रोसेसिंग का उपयोग कर पाएंगे।


इस बीच, पायथन की "बैटरी शामिल" दर्शन कुछ अच्छी खबर लाता है: कोड लिखना बहुत आसान है जो थ्रेड और प्रक्रियाओं के बीच एक-लाइनर परिवर्तन के साथ आगे और पीछे स्विच किया जा सकता है।

यदि आप अपना कोड स्व-निहित "नौकरियों" के संदर्भ में डिज़ाइन करते हैं जो इनपुट और आउटपुट के अलावा अन्य नौकरियों (या मुख्य कार्यक्रम) के साथ कुछ भी साझा नहीं करते हैं, तो आप concurrent.futuresइस तरह से एक थ्रेड पूल के आसपास अपना कोड लिखने के लिए पुस्तकालय का उपयोग कर सकते हैं :

with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor:
    executor.submit(job, argument)
    executor.map(some_function, collection_of_independent_things)
    # ...

आप उन नौकरियों के परिणाम भी प्राप्त कर सकते हैं और उन्हें आगे की नौकरियों के लिए पास कर सकते हैं, निष्पादन के क्रम में या पूर्ण होने के क्रम में चीजों की प्रतीक्षा कर सकते हैं, आदि; Futureविवरण के लिए ऑब्जेक्ट पर अनुभाग पढ़ें ।

अब, अगर यह पता चला कि आपका प्रोग्राम लगातार 100% सीपीयू का उपयोग कर रहा है, और अधिक थ्रेड्स जोड़ने से यह धीमा हो जाता है, तो आप जीआईएल समस्या में चल रहे हैं, इसलिए आपको प्रक्रियाओं पर स्विच करने की आवश्यकता है। आपको बस उस पहली पंक्ति को बदलना होगा:

with concurrent.futures.ProcessPoolExecutor(max_workers=4) as executor:

एकमात्र वास्तविक चेतावनी यह है कि आपकी नौकरियों की दलीलें और वापसी के मूल्यों को प्रयोग करने योग्य क्रॉस-प्रोसेस होने के लिए पिकलेबल होना चाहिए (और अचार को बहुत अधिक समय या मेमोरी नहीं लेना है)। आमतौर पर यह कोई समस्या नहीं है, लेकिन कभी-कभी यह होता है।


लेकिन क्या होगा अगर आपकी नौकरियां स्व-निहित नहीं हो सकती हैं? यदि आप अपने कोड को उन नौकरियों के संदर्भ में डिज़ाइन कर सकते हैं जो एक से दूसरे में संदेश भेजते हैं, तो यह अभी भी बहुत आसान है। आप उपयोग करना पड़ सकता है threading.Threadया multiprocessing.Processबजाय पूल पर भरोसा करने की। और आपको स्पष्ट रूप से ऑब्जेक्ट बनाने queue.Queueया बनाने होंगे multiprocessing.Queue। (अन्य विकल्प के बहुत सारे हैं- पाइप, सॉकेट, फाइल झुंडों के साथ, ... लेकिन मुद्दा यह है, कि आपको मैन्युअल रूप से कुछ करना होगा अगर एक्जिक्यूटर का स्वचालित जादू अपर्याप्त है।)

लेकिन क्या होगा अगर आप संदेश पास करने पर भी भरोसा नहीं कर सकते हैं? क्या होगा यदि आपको एक ही संरचना को म्यूट करने के लिए दो नौकरियों की आवश्यकता है, और एक-दूसरे के बदलाव देखें? उस स्थिति में, आपको मैन्युअल सिंक्रोनाइज़ेशन (ताले, सेमीफ़ोर्स, शर्तें, आदि) करने की आवश्यकता होगी और, यदि आप प्रक्रियाओं का उपयोग करना चाहते हैं, तो स्पष्ट साझा-मेमोरी ऑब्जेक्ट्स को बूट करने के लिए। यह तब होता है जब मल्टीथ्रेडिंग (या मल्टीप्रोसेसिंग) मुश्किल हो जाता है। अगर तुम इससे बच सकते हो, महान; यदि आप नहीं कर सकते हैं, तो आपको एक एसओ उत्तर में डाल देने से अधिक पढ़ने की आवश्यकता होगी।


एक टिप्पणी से, आप जानना चाहते थे कि पायथन में धागे और प्रक्रियाओं के बीच क्या अंतर है। वास्तव में, यदि आप Giulio फ्रेंको के जवाब और मेरा और हमारे सभी लिंक पढ़ते हैं, तो उसे सब कुछ कवर करना चाहिए ... लेकिन एक सारांश निश्चित रूप से उपयोगी होगा, इसलिए यहां जाता है:

  1. थ्रेड्स डिफ़ॉल्ट रूप से डेटा साझा करते हैं; प्रक्रियाएं नहीं।
  2. (1) के परिणामस्वरूप, प्रक्रियाओं के बीच डेटा भेजना आम तौर पर अचार बनाना और इसे अनप्लिक करना होता है। **
  3. (1) के एक और परिणाम के रूप में, प्रक्रियाओं के बीच डेटा को सीधे साझा करने के लिए आम तौर पर इसे निम्न-स्तरीय स्वरूपों जैसे मूल्य, सरणी, और ctypesप्रकारों में डालने की आवश्यकता होती है ।
  4. प्रक्रियाएँ GIL के अधीन नहीं हैं।
  5. कुछ प्लेटफार्मों (मुख्य रूप से विंडोज) पर, प्रक्रियाएं बनाने और नष्ट करने के लिए बहुत अधिक महंगी हैं।
  6. प्रक्रियाओं पर कुछ अतिरिक्त प्रतिबंध हैं, जिनमें से कुछ विभिन्न प्लेटफार्मों पर अलग हैं। देखें दिशा-निर्देशों प्रोग्रामिंग जानकारी के लिए।
  7. threadingमॉड्यूल के सुविधाओं में से कुछ भी नहीं है multiprocessingमॉड्यूल। (आप multiprocessing.dummyथ्रेड्स के शीर्ष पर अधिकांश लापता एपीआई प्राप्त करने के लिए उपयोग कर सकते हैं , या आप उच्च-स्तरीय मॉड्यूल का उपयोग कर सकते हैं जैसे कि concurrent.futuresऔर इसके बारे में चिंता न करें।)

* यह वास्तव में अजगर नहीं है, भाषा, जिसमें यह मुद्दा है, लेकिन सीपीथॉन, उस भाषा का "मानक" कार्यान्वयन है। कुछ अन्य कार्यान्वयनों में एक GIL नहीं है, जैसे कि Jython।

** यदि आप मल्टीप्रोसेसिंग के लिए कांटा शुरू करने की विधि का उपयोग कर रहे हैं - जिसे आप अधिकांश गैर-विंडोज प्लेटफार्मों पर कर सकते हैं - प्रत्येक बच्चे की प्रक्रिया में माता-पिता के पास कोई भी संसाधन होता है जब बच्चा शुरू किया गया था, जो बच्चों को डेटा पास करने का एक और तरीका हो सकता है।


धन्यवाद, लेकिन मुझे यकीन नहीं है कि मैं सब कुछ समझ गया हूं। वैसे भी मैं इसे सीखने के उद्देश्यों के लिए थोड़ा करने की कोशिश कर रहा हूं, और थोड़ा सा क्योंकि धागे के एक भोले उपयोग के साथ मैंने अपने कोड की गति को आधा कर दिया (एक ही समय में 1000 से अधिक धागे शुरू करते हैं, प्रत्येक एक बाहरी ऐप को बुलाता है .. यह संतृप्त करता है। सीपीयू, फिर भी गति में एक x2 वृद्धि है)। मुझे लगता है कि धागे को
स्मार्ट तरीके

3
@LucaCerone: आह, अगर आपका कोड अपना अधिकांश समय बाहरी कार्यक्रमों में इंतजार करने में बिताता है, तो हाँ, यह थ्रेडिंग से लाभ होगा। अच्छी बात। मुझे समझाने का उत्तर संपादित करने दीजिए।
अगस्ता

2
@LucaCerone: इस बीच, आप किन भागों को नहीं समझते हैं? आपके द्वारा शुरू किए जा रहे ज्ञान के स्तर को जाने बिना, एक अच्छा उत्तर लिखना मुश्किल है ... लेकिन कुछ प्रतिक्रिया के साथ, शायद हम आपके लिए और भविष्य के पाठकों के लिए भी कुछ ऐसा कर सकते हैं।
abarnert

3
@LucaCerone आपको यहां मल्टीप्रोसेसिंग के लिए पीईपी पढ़ना चाहिए । यह थ्रेडिंग बनाम मल्टीप्रोसेसिंग के समय और उदाहरण देता है।
mr2ert

1
@LucaCerone: यदि वह वस्तु जिस विधि से बंधी हुई है, उसमें कोई जटिल स्थिति नहीं है, तो अचार के मुद्दे के लिए सबसे सरल समाधान यह है कि वह एक बेवकूफ आवरण कार्य लिखता है जो वस्तु उत्पन्न करता है और उसकी विधि कहता है। यह तो है जटिल राज्य है, तो आप शायद यह picklable बनाने की जरूरत है (जो बहुत आसान है, pickleडॉक्स यह समझाने), और फिर ज्यादा से ज्यादा अपने बेवकूफ आवरण है def wrapper(obj, *args): return obj.wrapper(*args)
ab Augert

32

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

यदि आप एक संगणना को समानांतर बनाना चाहते हैं, तो आपको शायद मल्टीथ्रेडिंग की आवश्यकता होगी, क्योंकि आप संभवतः उसी मेमोरी पर सहयोग करना चाहते हैं।

प्रदर्शन के बारे में बोलते हुए, थ्रेड प्रक्रियाओं की तुलना में बनाने और प्रबंधित करने के लिए तेज़ होते हैं (क्योंकि ओएस को पूरी तरह से नया वर्चुअल मेमोरी क्षेत्र आवंटित करने की आवश्यकता नहीं है), और अंतर-थ्रेड संचार आमतौर पर अंतर-प्रक्रिया संचार की तुलना में तेज़ है। लेकिन थ्रेड्स प्रोग्राम के लिए कठिन हैं। थ्रेड्स एक-दूसरे के साथ हस्तक्षेप कर सकते हैं, और एक-दूसरे की मेमोरी को लिख सकते हैं, लेकिन ऐसा होने का तरीका हमेशा स्पष्ट नहीं होता है (कई कारकों के कारण, मुख्य रूप से निर्देश का पुन: परीक्षण और मेमोरी कैशिंग), और इसलिए आपको अभिगम को नियंत्रित करने के लिए सिंक्रोनाइज़ेशन प्राइमेटिव्स की आवश्यकता है। अपने चर के लिए।


12
यह जीआईएल के बारे में कुछ बहुत महत्वपूर्ण जानकारी गायब है, जो इसे भ्रामक बनाता है।
abarnert

1
@ mr2ert: हाँ, यह संक्षेप में बहुत महत्वपूर्ण जानकारी है। :) लेकिन यह उससे थोड़ा अधिक जटिल है, यही वजह है कि मैंने एक अलग उत्तर लिखा है।
abarnert

2
मुझे लगा कि मैंने टिप्पणी करते हुए कहा है कि @abarnert सही है, और मैं यहाँ जवाब देने में GIL के बारे में भूल गया। तो यह उत्तर गलत है, आपको इसे नहीं छोड़ना चाहिए।
Giulio Franco

6
मैंने इस उत्तर को अस्वीकार कर दिया क्योंकि यह अभी भी बिल्कुल भी जवाब नहीं देता है कि पायथन threadingऔर के बीच क्या अंतर है multiprocessing
अंती हापाला

मैंने पढ़ा है कि हर प्रक्रिया के लिए एक GIL है। लेकिन क्या सभी प्रक्रियाएं एक ही अजगर दुभाषिया का उपयोग करती हैं या क्या प्रति धागे के लिए अलग दुभाषिया है?
चर

3

मेरा मानना ​​है कि यह लिंक आपके प्रश्न का सुरुचिपूर्ण ढंग से उत्तर देता है।

कम होने के लिए, यदि आपकी एक उप-समस्या को समाप्त करने के लिए इंतजार करना पड़ता है, तो मल्टीथ्रेडिंग अच्छा है (उदाहरण के लिए I / O भारी संचालन में); इसके विपरीत, यदि आपकी उप-समस्याएं वास्तव में एक ही समय में हो सकती हैं, तो मल्टीप्रोसेसिंग का सुझाव दिया जाता है। हालाँकि, आप अपनी संख्या से अधिक प्रक्रियाएँ नहीं बनाएंगे।


3

पायथन प्रलेखन उद्धरण

मैंने प्रक्रिया बनाम थ्रेड्स और GIL के बारे में मुख्य पायथन डॉक्यूमेंटेशन कोट्स को हाइलाइट किया है: CPython में ग्लोबल इंटरप्रेटर लॉक (GIL) क्या है?

प्रक्रिया बनाम धागा प्रयोग

मैंने अंतर को अधिक स्पष्ट रूप से दिखाने के लिए थोड़ा सा बेंचमार्किंग किया।

बेंचमार्क में, मैंने 8 हाइपरथ्रेड सीपीयू पर सीपीयू और आईओ बाउंड के विभिन्न नंबरों के लिए काम किया । प्रति थ्रेड की आपूर्ति का काम हमेशा एक जैसा होता है, जैसे कि अधिक थ्रेड्स का अर्थ है कुल आपूर्ति का काम।

परिणाम थे:

यहाँ छवि विवरण दर्ज करें

प्लॉट डेटा

निष्कर्ष:

  • सीपीयू बाउंड वर्क के लिए, बहुप्रतिभा हमेशा जीआईएल के कारण तेजी से होती है

  • IO बाउंड वर्क के लिए। दोनों बिल्कुल समान गति वाले हैं

  • थ्रेड्स केवल 8x के बजाय लगभग 8x तक के पैमाने पर हैं क्योंकि मैं 8 हाइपरथ्रेड मशीन पर हूं।

    इसके विपरीत एक C POSIX CPU-बाउंड कार्य जो अपेक्षित 8x स्पीडअप तक पहुंचता है: समय (1) के आउटपुट में 'वास्तविक', 'उपयोगकर्ता' और 'sys' का क्या अर्थ है?

    TODO: मैं इसका कारण नहीं जानता, प्ले में आने वाले अन्य पायथन अक्षमताएं होनी चाहिए।

टेस्ट कोड:

#!/usr/bin/env python3

import multiprocessing
import threading
import time
import sys

def cpu_func(result, niters):
    '''
    A useless CPU bound function.
    '''
    for i in range(niters):
        result = (result * result * i + 2 * result * i * i + 3) % 10000000
    return result

class CpuThread(threading.Thread):
    def __init__(self, niters):
        super().__init__()
        self.niters = niters
        self.result = 1
    def run(self):
        self.result = cpu_func(self.result, self.niters)

class CpuProcess(multiprocessing.Process):
    def __init__(self, niters):
        super().__init__()
        self.niters = niters
        self.result = 1
    def run(self):
        self.result = cpu_func(self.result, self.niters)

class IoThread(threading.Thread):
    def __init__(self, sleep):
        super().__init__()
        self.sleep = sleep
        self.result = self.sleep
    def run(self):
        time.sleep(self.sleep)

class IoProcess(multiprocessing.Process):
    def __init__(self, sleep):
        super().__init__()
        self.sleep = sleep
        self.result = self.sleep
    def run(self):
        time.sleep(self.sleep)

if __name__ == '__main__':
    cpu_n_iters = int(sys.argv[1])
    sleep = 1
    cpu_count = multiprocessing.cpu_count()
    input_params = [
        (CpuThread, cpu_n_iters),
        (CpuProcess, cpu_n_iters),
        (IoThread, sleep),
        (IoProcess, sleep),
    ]
    header = ['nthreads']
    for thread_class, _ in input_params:
        header.append(thread_class.__name__)
    print(' '.join(header))
    for nthreads in range(1, 2 * cpu_count):
        results = [nthreads]
        for thread_class, work_size in input_params:
            start_time = time.time()
            threads = []
            for i in range(nthreads):
                thread = thread_class(work_size)
                threads.append(thread)
                thread.start()
            for i, thread in enumerate(threads):
                thread.join()
            results.append(time.time() - start_time)
        print(' '.join('{:.6e}'.format(result) for result in results))

समान निर्देशिका पर GitHub अपस्ट्रीम + प्लॉटिंग कोड

उबंटू 18.10 पर परीक्षण किया गया, CPU के साथ लेनोवो थिंकपैड P51 लैपटॉप में पाइथन 3.6.7, इंटेल कोर i7-7820HQ CPU (4 कोर / 8 धागे), RAM: 2x सैमसंग M471A2K43BB1-CRC (2x 16GiB), SSD: Samsung MZVLB512HAJQ- 000L7 (3,000 एमबी / एस)।

कल्पना करें कि कौन से धागे एक निश्चित समय पर चल रहे हैं

इस पोस्ट https://rohanvarma.me/GIL/ ने मुझे सिखाया कि जब भी कोई थ्रेड target=तर्क-वितर्क केthreading.Thread लिए निर्धारित हो और उसी के लिए आप कॉलबैक चला सकें multiprocessing.Process

यह हमें यह देखने की अनुमति देता है कि प्रत्येक समय कौन सा धागा चलता है। जब ऐसा किया जाता है, तो हम कुछ ऐसा देखेंगे (मैंने इस विशेष ग्राफ को बनाया):

            +--------------------------------------+
            + Active threads / processes           +
+-----------+--------------------------------------+
|Thread   1 |********     ************             |
|         2 |        *****            *************|
+-----------+--------------------------------------+
|Process  1 |***  ************** ******  ****      |
|         2 |** **** ****** ** ********* **********|
+-----------+--------------------------------------+
            + Time -->                             +
            +--------------------------------------+

जो दिखाएगा:

  • धागे पूरी तरह से जीआईएल द्वारा क्रमबद्ध हैं
  • प्रक्रियाएं समानांतर में चल सकती हैं

1

यहां अजगर 2.6.x के लिए कुछ प्रदर्शन डेटा हैं जो इस धारणा को चुनौती देने के लिए कहते हैं कि थ्रेडिंग अधिक प्रदर्शन योग्य है जो आईओ-बाउंड परिदृश्यों में मल्टीप्रोसेसिंग है। ये परिणाम 40-प्रोसेसर आईबीएम सिस्टम x3650 एम 4 बीडी से हैं।

आईओ-बाउंड प्रोसेसिंग: प्रोसेस पूल ने थ्रेड पूल से बेहतर प्रदर्शन किया

>>> do_work(50, 300, 'thread','fileio')
do_work function took 455.752 ms

>>> do_work(50, 300, 'process','fileio')
do_work function took 319.279 ms

सीपीयू-बाउंड प्रोसेसिंग: प्रोसेस पूल थ्रेड पूल से बेहतर प्रदर्शन करता है

>>> do_work(50, 2000, 'thread','square')
do_work function took 338.309 ms

>>> do_work(50, 2000, 'process','square')
do_work function took 287.488 ms

ये कठोर परीक्षण नहीं हैं, लेकिन वे मुझे बताते हैं कि थ्रेडिंग की तुलना में मल्टीप्रोसेसिंग पूरी तरह से अनुचित नहीं है।

उपरोक्त परीक्षणों के लिए इंटरैक्टिव पाइथन कंसोल में प्रयुक्त कोड

from multiprocessing import Pool
from multiprocessing.pool import ThreadPool
import time
import sys
import os
from glob import glob

text_for_test = str(range(1,100000))

def fileio(i):
 try :
  os.remove(glob('./test/test-*'))
 except : 
  pass
 f=open('./test/test-'+str(i),'a')
 f.write(text_for_test)
 f.close()
 f=open('./test/test-'+str(i),'r')
 text = f.read()
 f.close()


def square(i):
 return i*i

def timing(f):
 def wrap(*args):
  time1 = time.time()
  ret = f(*args)
  time2 = time.time()
  print '%s function took %0.3f ms' % (f.func_name, (time2-time1)*1000.0)
  return ret
 return wrap

result = None

@timing
def do_work(process_count, items, process_type, method) :
 pool = None
 if process_type == 'process' :
  pool = Pool(processes=process_count)
 else :
  pool = ThreadPool(processes=process_count)
 if method == 'square' : 
  multiple_results = [pool.apply_async(square,(a,)) for a in range(1,items)]
  result = [res.get()  for res in multiple_results]
 else :
  multiple_results = [pool.apply_async(fileio,(a,)) for a in range(1,items)]
  result = [res.get()  for res in multiple_results]


do_work(50, 300, 'thread','fileio')
do_work(50, 300, 'process','fileio')

do_work(50, 2000, 'thread','square')
do_work(50, 2000, 'process','square')

मैंने आपके कोड का उपयोग किया है ( ग्लोब भाग को हटा दिया है ) और पायथन 2.6.6 के साथ यह दिलचस्प परिणाम पाया है।>>> do_work(50, 300, 'thread', 'fileio') --> 237.557 ms >>> do_work(50, 300, 'process', 'fileio') --> 323.963 ms >>> do_work(50, 2000, 'thread', 'square') --> 232.082 ms >>> do_work(50, 2000, 'process', 'square') --> 282.785 ms
एलन गैरिडो

-5

खैर, ज्यादातर सवाल का जवाब Giulio Franco द्वारा दिया गया है। मैं उपभोक्ता-उत्पादक समस्या के बारे में विस्तार से बताऊंगा, जो मुझे लगता है कि आपके समाधान के लिए एक मल्टीथ्रेड ऐप का उपयोग करने के लिए आपको सही रास्ते पर खड़ा करेगा।

fill_count = Semaphore(0) # items produced
empty_count = Semaphore(BUFFER_SIZE) # remaining space
buffer = Buffer()

def producer(fill_count, empty_count, buffer):
    while True:
        item = produceItem()
        empty_count.down();
        buffer.push(item)
        fill_count.up()

def consumer(fill_count, empty_count, buffer):
    while True:
        fill_count.down()
        item = buffer.pop()
        empty_count.up()
        consume_item(item)

आप सिंक्रनाइज़ेशन प्राथमिकताओं पर और अधिक पढ़ सकते हैं:

 http://linux.die.net/man/7/sem_overview
 http://docs.python.org/2/library/threading.html

स्यूडोकोड ऊपर है। मुझे लगता है कि आपको अधिक संदर्भ प्राप्त करने के लिए निर्माता-उपभोक्ता-समस्या की खोज करनी चाहिए।


खेद निर्दोष, लेकिन यह मेरे लिए सी ++ लगता है? लिंक के लिए धन्यवाद :)
22

दरअसल, मल्टीप्रोसेसिंग और मल्टीथ्रेडिंग के पीछे के विचार भाषा स्वतंत्र हैं। समाधान उपरोक्त कोड के समान होगा।
निर्दोष

2
यह C ++ नहीं है; यह pseudocode है (या यह एक C- जैसे सिंटैक्स के साथ ज्यादातर-डायनामिक-टाइप की गई भाषा के लिए कोड है। कहा जा रहा है, मुझे लगता है कि पायथन उपयोगकर्ताओं को पढ़ाने के लिए Python-like pseudocode लिखना अधिक उपयोगी है। विशेष रूप से पायथन-जैसे pededocode के बाद से। बाहर चलने योग्य कोड हो जाता है, या कम से कम इसके करीब, जो कि सी-
लाइक

मैंने इसे पायथन-जैसे स्यूडोकोड के रूप में फिर से लिखा है (वैश्विक वस्तुओं का उपयोग करने के बजाय OO और पासिंग पैरामीटर का उपयोग करके); यदि आपको लगता है कि चीजों को कम स्पष्ट करता है, तो वापस लौटने के लिए स्वतंत्र महसूस करें।
22

इसके अलावा, यह ध्यान देने योग्य है कि पायथन stdlib में एक सिंक्रनाइज़ कतार बनाई गई है जो इन सभी विवरणों को लपेटती है, और इसके धागे और प्रक्रिया एपीआई एपीआई अमूर्त चीजों को और भी आगे बढ़ाती है । यह निश्चित रूप से समझने योग्य है कि कैसे कवर्स के तहत सिंक्रनाइज़ की गई कतारें काम करती हैं, लेकिन आपको शायद ही कभी खुद को लिखने की आवश्यकता होगी।
23
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.