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) के परिणामस्वरूप, प्रक्रियाओं के बीच डेटा भेजना आम तौर पर अचार बनाना और इसे अनप्लिक करना होता है। **
- (1) के एक और परिणाम के रूप में, प्रक्रियाओं के बीच डेटा को सीधे साझा करने के लिए आम तौर पर इसे निम्न-स्तरीय स्वरूपों जैसे मूल्य, सरणी, और
ctypes
प्रकारों में डालने की आवश्यकता होती है ।
- प्रक्रियाएँ GIL के अधीन नहीं हैं।
- कुछ प्लेटफार्मों (मुख्य रूप से विंडोज) पर, प्रक्रियाएं बनाने और नष्ट करने के लिए बहुत अधिक महंगी हैं।
- प्रक्रियाओं पर कुछ अतिरिक्त प्रतिबंध हैं, जिनमें से कुछ विभिन्न प्लेटफार्मों पर अलग हैं। देखें दिशा-निर्देशों प्रोग्रामिंग जानकारी के लिए।
threading
मॉड्यूल के सुविधाओं में से कुछ भी नहीं है multiprocessing
मॉड्यूल। (आप multiprocessing.dummy
थ्रेड्स के शीर्ष पर अधिकांश लापता एपीआई प्राप्त करने के लिए उपयोग कर सकते हैं , या आप उच्च-स्तरीय मॉड्यूल का उपयोग कर सकते हैं जैसे कि concurrent.futures
और इसके बारे में चिंता न करें।)
* यह वास्तव में अजगर नहीं है, भाषा, जिसमें यह मुद्दा है, लेकिन सीपीथॉन, उस भाषा का "मानक" कार्यान्वयन है। कुछ अन्य कार्यान्वयनों में एक GIL नहीं है, जैसे कि Jython।
** यदि आप मल्टीप्रोसेसिंग के लिए कांटा शुरू करने की विधि का उपयोग कर रहे हैं - जिसे आप अधिकांश गैर-विंडोज प्लेटफार्मों पर कर सकते हैं - प्रत्येक बच्चे की प्रक्रिया में माता-पिता के पास कोई भी संसाधन होता है जब बच्चा शुरू किया गया था, जो बच्चों को डेटा पास करने का एक और तरीका हो सकता है।
Thread
मॉड्यूल (_thread
अजगर 3.x में कहा जाता है)। सच कहूं तो, मैंने खुद मतभेदों को कभी नहीं समझा है ...