मल्टीथ्रेडिंग Python उदाहरण के साथ: GIL सीखें Python
एक धागा क्या है?
थ्रेड समवर्ती प्रोग्रामिंग पर निष्पादन की एक इकाई है। मल्टीथ्रेडिंग एक ऐसी तकनीक है जो CPU को एक ही समय में एक प्रक्रिया के कई कार्यों को निष्पादित करने की अनुमति देती है। ये थ्रेड अपने प्रक्रिया संसाधनों को साझा करते हुए व्यक्तिगत रूप से निष्पादित कर सकते हैं।
प्रक्रिया क्या है?
एक प्रक्रिया मूल रूप से निष्पादन में प्रोग्राम है। जब आप अपने कंप्यूटर में कोई एप्लिकेशन शुरू करते हैं (जैसे ब्राउज़र या टेक्स्ट एडिटर), तो ऑपरेटिंग सिस्टम एक प्रोग्राम बनाता है। प्रक्रिया.
मल्टीथ्रेडिंग क्या है? Python?
मल्टीथ्रेडिंग Python प्रोग्रामिंग एक जानी-मानी तकनीक है जिसमें एक प्रक्रिया में कई थ्रेड्स अपने डेटा स्पेस को मुख्य थ्रेड के साथ साझा करते हैं जो थ्रेड्स के भीतर सूचना साझाकरण और संचार को आसान और कुशल बनाता है। थ्रेड्स प्रक्रियाओं की तुलना में हल्के होते हैं। मल्टी थ्रेड्स अपने प्रक्रिया संसाधनों को साझा करते हुए व्यक्तिगत रूप से निष्पादित हो सकते हैं। मल्टीथ्रेडिंग का उद्देश्य एक ही समय में कई कार्यों और फ़ंक्शन सेल को चलाना है।
मल्टीप्रोसेसिंग क्या है?
बहु आपको एक साथ कई असंबंधित प्रक्रियाएँ चलाने की अनुमति देता है। ये प्रक्रियाएँ अपने संसाधनों को साझा नहीं करती हैं और IPC के माध्यम से संचार करती हैं।
Python मल्टीथ्रेडिंग बनाम मल्टीप्रोसेसिंग
प्रक्रियाओं और थ्रेड्स को समझने के लिए, इस परिदृश्य पर विचार करें: आपके कंप्यूटर पर एक .exe फ़ाइल एक प्रोग्राम है। जब आप इसे खोलते हैं, तो OS इसे मेमोरी में लोड करता है, और CPU इसे निष्पादित करता है। प्रोग्राम का वह उदाहरण जो अब चल रहा है उसे प्रक्रिया कहा जाता है।
प्रत्येक प्रक्रिया के दो मूलभूत घटक होंगे:
- कोड
- आँकड़े
अब, एक प्रक्रिया में एक या एक से अधिक उप-भाग हो सकते हैं जिन्हें कहा जाता है धागे। यह ओएस आर्किटेक्चर पर निर्भर करता है। आप थ्रेड को प्रक्रिया के एक भाग के रूप में सोच सकते हैं जिसे ऑपरेटिंग सिस्टम द्वारा अलग से निष्पादित किया जा सकता है।
दूसरे शब्दों में, यह निर्देशों की एक धारा है जिसे ओएस द्वारा स्वतंत्र रूप से चलाया जा सकता है। एक ही प्रक्रिया के भीतर थ्रेड उस प्रक्रिया के डेटा को साझा करते हैं और समानांतरता की सुविधा के लिए एक साथ काम करने के लिए डिज़ाइन किए गए हैं।
मल्टीथ्रेडिंग का उपयोग क्यों करें?
मल्टीथ्रेडिंग आपको किसी एप्लिकेशन को कई उप-कार्यों में विभाजित करने और इन कार्यों को एक साथ चलाने की अनुमति देता है। यदि आप मल्टीथ्रेडिंग का सही तरीके से उपयोग करते हैं, तो आपके एप्लिकेशन की गति, प्रदर्शन और रेंडरिंग सभी में सुधार हो सकता है।
Python बहु सूत्रण
Python मल्टीप्रोसेसिंग और मल्टीथ्रेडिंग दोनों के लिए निर्माण का समर्थन करता है। इस ट्यूटोरियल में, आप मुख्य रूप से कार्यान्वयन पर ध्यान केंद्रित करेंगे बहु पायथन के साथ अनुप्रयोग। दो मुख्य मॉड्यूल हैं जिनका उपयोग थ्रेड्स को संभालने के लिए किया जा सकता है Python:
- RSI धागा मॉड्यूल, और
- RSI सूत्रण मॉड्यूल
हालाँकि, पायथन में ग्लोबल इंटरप्रेटर लॉक (GIL) नाम की भी कोई चीज़ होती है। यह बहुत ज़्यादा परफॉरमेंस बढ़ाने की अनुमति नहीं देता है और हो सकता है कि यह बहुत ज़्यादा हो। को कम करने कुछ मल्टीथ्रेडेड अनुप्रयोगों का प्रदर्शन। आप इस ट्यूटोरियल के आगामी अनुभागों में इसके बारे में सब कुछ सीखेंगे।
थ्रेड और थ्रेडिंग मॉड्यूल
इस ट्यूटोरियल में आप जिन दो मॉड्यूल के बारे में जानेंगे वे हैं धागा मॉड्यूल और थ्रेडिंग मॉड्यूल.
हालाँकि, थ्रेड मॉड्यूल को लंबे समय से हटा दिया गया है। Python 3, इसे अप्रचलित घोषित कर दिया गया है और इसे केवल इस रूप में ही उपयोग किया जा सकता है __धागा पिछड़ी संगतता के लिए।
आपको उच्च-स्तर का उपयोग करना चाहिए सूत्रण उन अनुप्रयोगों के लिए मॉड्यूल जिन्हें आप तैनात करना चाहते हैं। थ्रेड मॉड्यूल को केवल शैक्षिक उद्देश्यों के लिए यहां शामिल किया गया है।
थ्रेड मॉड्यूल
इस मॉड्यूल का उपयोग करके नया थ्रेड बनाने का सिंटैक्स इस प्रकार है:
thread.start_new_thread(function_name, arguments)
ठीक है, अब आपने कोडिंग शुरू करने के लिए बुनियादी सिद्धांत को कवर कर लिया है। तो, अपना खोलें IDLE या नोटपैड लें और निम्नलिखित टाइप करें:
import time import _thread def thread_test(name, wait): i = 0 while i <= 3: time.sleep(wait) print("Running %s\n" %name) i = i + 1 print("%s has finished execution" %name) if __name__ == "__main__": _thread.start_new_thread(thread_test, ("First Thread", 1)) _thread.start_new_thread(thread_test, ("Second Thread", 2)) _thread.start_new_thread(thread_test, ("Third Thread", 3))
फ़ाइल को सेव करें और प्रोग्राम को चलाने के लिए F5 दबाएँ। अगर सब कुछ सही तरीके से किया गया है, तो आपको यह आउटपुट दिखाई देगा:
आप आगामी अनुभागों में रेस स्थितियों और उन्हें कैसे संभालना है, इसके बारे में अधिक जानेंगे
कोड स्पष्टीकरण
- ये कथन समय और थ्रेड मॉड्यूल को आयात करते हैं जिनका उपयोग निष्पादन और देरी को संभालने के लिए किया जाता है Python धागे।
- यहाँ, आपने एक फ़ंक्शन परिभाषित किया है जिसे थ्रेड_टेस्ट, जिसे बुलाया जाएगा नया_धागा_शुरू_करें विधि। फ़ंक्शन चार पुनरावृत्तियों के लिए while लूप चलाता है और उस थ्रेड का नाम प्रिंट करता है जिसने इसे कॉल किया था। एक बार पुनरावृत्ति पूरी हो जाने पर, यह एक संदेश प्रिंट करता है जिसमें कहा गया है कि थ्रेड ने निष्पादन समाप्त कर दिया है।
- यह आपके प्रोग्राम का मुख्य भाग है। यहाँ, आप बस कॉल करते हैं नया_धागा_शुरू_करें के साथ विधि थ्रेड_टेस्ट फ़ंक्शन को तर्क के रूप में बदलें। यह आपके द्वारा तर्क के रूप में पास किए गए फ़ंक्शन के लिए एक नया थ्रेड बनाएगा और इसे निष्पादित करना शुरू कर देगा। ध्यान दें कि आप इसे (थ्रेड) से बदल सकते हैं_test) को किसी अन्य फ़ंक्शन के साथ जोड़ें जिसे आप थ्रेड के रूप में चलाना चाहते हैं।
थ्रेडिंग मॉड्यूल
यह मॉड्यूल पायथन में थ्रेडिंग का उच्च-स्तरीय कार्यान्वयन है और मल्टीथ्रेडेड अनुप्रयोगों के प्रबंधन के लिए वास्तविक मानक है। थ्रेड मॉड्यूल की तुलना में यह कई प्रकार की सुविधाएँ प्रदान करता है।
इस मॉड्यूल में परिभाषित कुछ उपयोगी कार्यों की सूची यहां दी गई है:
फ़ंक्शन का नाम | विवरण |
---|---|
सक्रिय गणना() | की गिनती लौटाता है धागा वस्तुएँ जो अभी भी जीवित हैं |
वर्तमान थ्रेड() | थ्रेड वर्ग का वर्तमान ऑब्जेक्ट लौटाता है। |
गणना करें() | सभी सक्रिय थ्रेड ऑब्जेक्ट्स को सूचीबद्ध करता है. |
isडेमन() | यदि थ्रेड डेमॉन है तो true लौटाता है. |
जिंदा है() | यदि थ्रेड अभी भी जीवित है तो true लौटाता है। |
थ्रेड क्लास विधियाँ | |
शुरू() | किसी थ्रेड की गतिविधि शुरू करता है। इसे प्रत्येक थ्रेड के लिए केवल एक बार ही कॉल किया जाना चाहिए क्योंकि इसे कई बार कॉल करने पर रनटाइम त्रुटि आएगी। |
Daud() | यह विधि थ्रेड की गतिविधि को दर्शाती है और इसे थ्रेड क्लास का विस्तार करने वाले क्लास द्वारा ओवरराइड किया जा सकता है। |
में शामिल होने के () | यह अन्य कोड के निष्पादन को तब तक अवरुद्ध करता है जब तक कि वह थ्रेड समाप्त नहीं हो जाता जिस पर join() विधि को बुलाया गया था। |
बैकस्टोरी: थ्रेड क्लास
इससे पहले कि आप थ्रेडिंग मॉड्यूल का उपयोग करके मल्टीथ्रेडेड प्रोग्राम कोडिंग शुरू करें, थ्रेड क्लास के बारे में समझना महत्वपूर्ण है। थ्रेड क्लास प्राथमिक क्लास है जो पायथन में टेम्पलेट और थ्रेड के संचालन को परिभाषित करता है।
मल्टीथ्रेडेड पायथन अनुप्रयोग बनाने का सबसे आम तरीका एक क्लास घोषित करना है जो थ्रेड क्लास का विस्तार करता है और इसकी run() विधि को ओवरराइड करता है।
संक्षेप में, थ्रेड क्लास एक कोड अनुक्रम को दर्शाता है जो एक अलग तरीके से चलता है धागा नियंत्रण के।
इसलिए, मल्टीथ्रेडेड ऐप लिखते समय, आप निम्नलिखित कार्य करेंगे:
- एक वर्ग को परिभाषित करें जो थ्रेड वर्ग का विस्तार करता है
- ओवरराइड किया __init__ निर्माता
- ओवरराइड किया Daud() तरीका
एक बार थ्रेड ऑब्जेक्ट बना लेने के बाद, शुरू() इस गतिविधि के निष्पादन को शुरू करने के लिए विधि का उपयोग किया जा सकता है और में शामिल होने के () विधि का उपयोग वर्तमान गतिविधि समाप्त होने तक अन्य सभी कोड को ब्लॉक करने के लिए किया जा सकता है।
अब, चलिए आपके पिछले उदाहरण को लागू करने के लिए थ्रेडिंग मॉड्यूल का उपयोग करके देखें। फिर से, अपना IDLE और निम्नलिखित टाइप करें:
import time import threading class threadtester (threading.Thread): def __init__(self, id, name, i): threading.Thread.__init__(self) self.id = id self.name = name self.i = i def run(self): thread_test(self.name, self.i, 5) print ("%s has finished execution " %self.name) def thread_test(name, wait, i): while i: time.sleep(wait) print ("Running %s \n" %name) i = i - 1 if __name__=="__main__": thread1 = threadtester(1, "First Thread", 1) thread2 = threadtester(2, "Second Thread", 2) thread3 = threadtester(3, "Third Thread", 3) thread1.start() thread2.start() thread3.start() thread1.join() thread2.join() thread3.join()
जब आप उपरोक्त कोड निष्पादित करेंगे तो यह आउटपुट होगा:
कोड स्पष्टीकरण
- यह भाग हमारे पिछले उदाहरण जैसा ही है। यहाँ, आप समय और थ्रेड मॉड्यूल आयात करते हैं जिनका उपयोग निष्पादन और देरी को संभालने के लिए किया जाता है। Python धागे।
- इस भाग में, आप थ्रेडटेस्टर नामक एक क्लास बना रहे हैं, जो थ्रेडटेस्टर को इनहेरिट या एक्सटेंड करता है। धागा थ्रेडिंग मॉड्यूल का क्लास। यह पायथन में थ्रेड बनाने के सबसे आम तरीकों में से एक है। हालाँकि, आपको केवल कंस्ट्रक्टर और को ओवरराइड करना चाहिए Daud() अपने ऐप में विधि। जैसा कि आप ऊपर दिए गए कोड नमूने में देख सकते हैं, __init__ विधि (कंस्ट्रक्टर) को ओवरराइड कर दिया गया है। इसी तरह, आपने भी ओवरराइड कर दिया है Daud() विधि। इसमें वह कोड होता है जिसे आप थ्रेड के अंदर निष्पादित करना चाहते हैं। इस उदाहरण में, आपने thread_test() फ़ंक्शन को कॉल किया है।
- यह thread_test() विधि है जो का मान लेती है i एक तर्क के रूप में, प्रत्येक पुनरावृत्ति पर इसे 1 से घटाता है और शेष कोड के माध्यम से तब तक लूप करता है जब तक कि i 0 नहीं हो जाता। प्रत्येक पुनरावृत्ति में, यह वर्तमान में निष्पादित थ्रेड का नाम प्रिंट करता है और प्रतीक्षा सेकंड के लिए सो जाता है (जिसे एक तर्क के रूप में भी लिया जाता है)।
- thread1 = threadtester(1, “पहला थ्रेड”, 1) यहाँ, हम एक थ्रेड बना रहे हैं और तीन पैरामीटर पास कर रहे हैं जिन्हें हमने __init__ में घोषित किया है। पहला पैरामीटर थ्रेड की आईडी है, दूसरा पैरामीटर थ्रेड का नाम है, और तीसरा पैरामीटर काउंटर है, जो यह निर्धारित करता है कि while लूप को कितनी बार चलाना चाहिए।
- thread2.start()स्टार्ट विधि का उपयोग थ्रेड के निष्पादन को शुरू करने के लिए किया जाता है। आंतरिक रूप से, start() फ़ंक्शन आपके क्लास की run() विधि को कॉल करता है।
- thread3.join() join() विधि अन्य कोड के निष्पादन को अवरुद्ध करती है और उस थ्रेड के समाप्त होने तक प्रतीक्षा करती है जिस पर उसे बुलाया गया था।
जैसा कि आप पहले से ही जानते हैं, एक ही प्रक्रिया में शामिल थ्रेड्स के पास उस प्रक्रिया की मेमोरी और डेटा तक पहुंच होती है। नतीजतन, अगर एक से ज़्यादा थ्रेड एक साथ डेटा को बदलने या एक्सेस करने की कोशिश करते हैं, तो त्रुटियाँ आ सकती हैं।
अगले अनुभाग में, आप विभिन्न प्रकार की जटिलताओं को देखेंगे जो तब सामने आ सकती हैं जब थ्रेड्स मौजूदा एक्सेस लेनदेन की जांच किए बिना डेटा और क्रिटिकल-सेक्शन तक पहुंचते हैं।
गतिरोध और दौड़ की स्थिति
डेडलॉक और रेस कंडीशन के बारे में जानने से पहले, समवर्ती प्रोग्रामिंग से संबंधित कुछ बुनियादी परिभाषाओं को समझना उपयोगी होगा:
- महत्वपूर्ण अनुभाग यह कोड का एक टुकड़ा है जो साझा चरों तक पहुंचता है या उन्हें संशोधित करता है और इसे परमाणु लेनदेन के रूप में निष्पादित किया जाना चाहिए।
- संदर्भ स्विच यह वह प्रक्रिया है जिसका पालन सीपीयू एक कार्य से दूसरे कार्य में परिवर्तन करने से पहले थ्रेड की स्थिति को संग्रहीत करने के लिए करता है ताकि बाद में इसे उसी बिंदु से फिर से शुरू किया जा सके।
गतिरोध
गतिरोध पायथन में समवर्ती/मल्टीथ्रेडेड एप्लिकेशन लिखते समय डेवलपर्स के सामने आने वाली सबसे भयावह समस्या है। डेडलॉक को समझने का सबसे अच्छा तरीका क्लासिक कंप्यूटर विज्ञान उदाहरण समस्या का उपयोग करना है जिसे के रूप में जाना जाता है भोजन Philoसोफ़र्स समस्या.
भोजन दार्शनिकों के लिए समस्या कथन इस प्रकार है:
जैसा कि चित्र में दिखाया गया है, पांच दार्शनिक एक गोल मेज पर स्पेगेटी (एक प्रकार का पास्ता) की पांच प्लेटों और पांच कांटों के साथ बैठे हैं।
किसी भी समय एक दार्शनिक या तो खा रहा होता है या सोच रहा होता है।
इसके अलावा, एक दार्शनिक को स्पेगेटी खाने से पहले अपने बगल के दो कांटे (यानी, बाएं और दाएं कांटे) लेने चाहिए। गतिरोध की समस्या तब होती है जब सभी पांच दार्शनिक एक साथ अपने दाएं कांटे उठाते हैं।
चूँकि सभी दार्शनिकों के पास एक-एक कांटा है, इसलिए वे सभी दूसरों के कांटा नीचे रखने का इंतज़ार करेंगे। नतीजतन, उनमें से कोई भी स्पेगेटी नहीं खा पाएगा।
इसी तरह, एक समवर्ती प्रणाली में, डेडलॉक तब होता है जब विभिन्न थ्रेड या प्रक्रियाएँ (दार्शनिक) एक ही समय में साझा सिस्टम संसाधनों (कांटे) को प्राप्त करने का प्रयास करते हैं। नतीजतन, किसी भी प्रक्रिया को निष्पादित करने का मौका नहीं मिलता क्योंकि वे किसी अन्य प्रक्रिया द्वारा रखे गए किसी अन्य संसाधन की प्रतीक्षा कर रहे होते हैं।
दौर कि शर्ते
रेस कंडीशन किसी प्रोग्राम की अवांछित स्थिति होती है जो तब होती है जब सिस्टम एक साथ दो या उससे ज़्यादा ऑपरेशन करता है। उदाहरण के लिए, इस सरल फ़ॉर लूप पर विचार करें:
i=0; # a global variable for x in range(100): print(i) i+=1;
यदि आप बनाते हैं n इस कोड को एक साथ चलाने वाले थ्रेड्स की संख्या के आधार पर, आप प्रोग्राम के निष्पादन समाप्त होने पर i (जो थ्रेड्स द्वारा साझा किया जाता है) का मान निर्धारित नहीं कर सकते। ऐसा इसलिए है क्योंकि वास्तविक मल्टीथ्रेडिंग वातावरण में, थ्रेड ओवरलैप हो सकते हैं, और i का मान जिसे थ्रेड द्वारा पुनर्प्राप्त और संशोधित किया गया था, बीच में बदल सकता है जब कोई अन्य थ्रेड इसे एक्सेस करता है।
ये दो मुख्य प्रकार की समस्याएं हैं जो मल्टीथ्रेडेड या वितरित पायथन एप्लिकेशन में हो सकती हैं। अगले भाग में, आप सीखेंगे कि थ्रेड्स को सिंक्रोनाइज़ करके इस समस्या को कैसे दूर किया जाए।
Syncह्रोनाइज़िंग धागे
रेस कंडीशन, डेडलॉक और अन्य थ्रेड-आधारित समस्याओं से निपटने के लिए, थ्रेडिंग मॉड्यूल प्रदान करता है ताला ऑब्जेक्ट। विचार यह है कि जब कोई थ्रेड किसी विशिष्ट संसाधन तक पहुँचना चाहता है, तो वह उस संसाधन के लिए लॉक प्राप्त करता है। एक बार जब कोई थ्रेड किसी विशेष संसाधन को लॉक कर देता है, तो कोई अन्य थ्रेड तब तक उस तक पहुँच नहीं सकता जब तक कि लॉक रिलीज़ न हो जाए। परिणामस्वरूप, संसाधन में परिवर्तन परमाणु होंगे, और रेस की स्थिति टल जाएगी।
लॉक एक निम्न-स्तरीय सिंक्रोनाइजेशन प्रिमिटिव है जिसे द्वारा कार्यान्वित किया जाता है __धागा मॉड्यूल। किसी भी समय, लॉक दो स्थितियों में से एक में हो सकता है: बंद or खुला हुआ। यह दो विधियों का समर्थन करता है:
- अधिग्रहण करना()जब लॉक-स्टेट अनलॉक हो जाता है, तो acquire() विधि को कॉल करने से स्टेट लॉक हो जाएगा और वापस आ जाएगा। हालाँकि, अगर स्टेट लॉक है, तो acquire() को कॉल तब तक ब्लॉक किया जाता है जब तक कि रिलीज़() विधि को किसी अन्य थ्रेड द्वारा कॉल नहीं किया जाता।
- मुक्त करना()रिलीज़() विधि का उपयोग स्टेट को अनलॉक करने के लिए किया जाता है, यानी लॉक को रिलीज़ करने के लिए। इसे किसी भी थ्रेड द्वारा कॉल किया जा सकता है, ज़रूरी नहीं कि वह थ्रेड ही हो जिसने लॉक हासिल किया हो।
यहां आपके ऐप्स में लॉक का उपयोग करने का एक उदाहरण दिया गया है। IDLE और निम्नलिखित टाइप करें:
import threading lock = threading.Lock() def first_function(): for i in range(5): lock.acquire() print ('lock acquired') print ('Executing the first funcion') lock.release() def second_function(): for i in range(5): lock.acquire() print ('lock acquired') print ('Executing the second funcion') lock.release() if __name__=="__main__": thread_one = threading.Thread(target=first_function) thread_two = threading.Thread(target=second_function) thread_one.start() thread_two.start() thread_one.join() thread_two.join()
अब, F5 दबाएँ। आपको कुछ इस तरह का आउटपुट दिखाई देगा:
कोड स्पष्टीकरण
- यहाँ, आप बस कॉल करके एक नया लॉक बना रहे हैं थ्रेडिंग.लॉक() फैक्ट्री फ़ंक्शन। आंतरिक रूप से, लॉक() प्लेटफ़ॉर्म द्वारा बनाए रखा गया सबसे प्रभावी कंक्रीट लॉक क्लास का एक उदाहरण देता है।
- पहले कथन में, आप acquire() विधि को कॉल करके लॉक प्राप्त करते हैं। जब लॉक प्रदान किया जाता है, तो आप प्रिंट करते हैं “लॉक अधिग्रहित” कंसोल पर। एक बार जब आप थ्रेड को चलाने के लिए जो कोड चाहते हैं उसका निष्पादन समाप्त हो जाता है, तो आप रिलीज़() विधि को कॉल करके लॉक को रिलीज़ करते हैं।
सिद्धांत तो ठीक है, लेकिन आप कैसे जानते हैं कि लॉक वास्तव में काम करता है? यदि आप आउटपुट को देखें, तो आप देखेंगे कि प्रत्येक प्रिंट स्टेटमेंट एक बार में ठीक एक लाइन प्रिंट कर रहा है। याद रखें कि, पहले के उदाहरण में, प्रिंट से आउटपुट बेतरतीब थे क्योंकि कई थ्रेड एक ही समय में प्रिंट() विधि तक पहुँच रहे थे। यहाँ, प्रिंट फ़ंक्शन को लॉक प्राप्त होने के बाद ही कॉल किया जाता है। इसलिए, आउटपुट एक-एक करके और लाइन दर लाइन प्रदर्शित होते हैं।
लॉक के अलावा, पायथन थ्रेड सिंक्रोनाइजेशन को संभालने के लिए कुछ अन्य तंत्रों का भी समर्थन करता है, जैसा कि नीचे सूचीबद्ध है:
- आरलॉक्स
- Semaphores
- स्थितियां
- घटनाएँ, और
- बाधाओं
ग्लोबल इंटरप्रेटर लॉक (और इससे कैसे निपटें)
पायथन के GIL के विवरण में जाने से पहले, आइए कुछ शब्दों को परिभाषित करें जो आगामी अनुभाग को समझने में उपयोगी होंगे:
- सीपीयू-बाउंड कोड: यह कोड के किसी भी भाग को संदर्भित करता है जिसे सीपीयू द्वारा सीधे निष्पादित किया जाएगा।
- I/O-बाउंड कोड: यह कोई भी कोड हो सकता है जो OS के माध्यम से फ़ाइल सिस्टम तक पहुँचता है
- CPython: यह संदर्भ है कार्यान्वयन of Python और इसे C और C में लिखे गए इंटरप्रेटर के रूप में वर्णित किया जा सकता है Python (प्रोग्रामिंग भाषा)
GIL क्या है? Python?
ग्लोबल इंटरप्रेटर लॉक (GIL) पायथन में एक प्रक्रिया लॉक या म्यूटेक्स होता है जिसका उपयोग प्रक्रियाओं से निपटने के दौरान किया जाता है। यह सुनिश्चित करता है कि एक थ्रेड एक समय में किसी विशेष संसाधन तक पहुँच सकता है और यह एक साथ ऑब्जेक्ट और बाइटकोड के उपयोग को भी रोकता है। इससे सिंगल-थ्रेडेड प्रोग्राम को प्रदर्शन में वृद्धि का लाभ मिलता है। पायथन में GIL बहुत सरल और लागू करने में आसान है।
लॉक का उपयोग यह सुनिश्चित करने के लिए किया जा सकता है कि किसी निश्चित समय पर केवल एक थ्रेड को ही किसी विशेष संसाधन तक पहुंच प्राप्त हो।
की विशेषताओं में से एक है Python यह प्रत्येक इंटरप्रेटर प्रक्रिया पर एक वैश्विक लॉक का उपयोग करता है, जिसका अर्थ है कि प्रत्येक प्रक्रिया पायथन इंटरप्रेटर को एक संसाधन के रूप में मानती है।
उदाहरण के लिए, मान लीजिए कि आपने एक पायथन प्रोग्राम लिखा है जो CPU और 'I/O' दोनों ऑपरेशन करने के लिए दो थ्रेड का उपयोग करता है। जब आप इस प्रोग्राम को निष्पादित करते हैं, तो यह होता है:
- पायथन इंटरप्रेटर एक नई प्रक्रिया बनाता है और थ्रेड्स को जन्म देता है
- जब थ्रेड-1 चलना शुरू करता है, तो वह सबसे पहले GIL प्राप्त करेगा और उसे लॉक करेगा।
- यदि थ्रेड-2 अभी निष्पादित करना चाहता है, तो उसे GIL के जारी होने की प्रतीक्षा करनी होगी, भले ही कोई अन्य प्रोसेसर मुक्त हो।
- अब, मान लीजिए कि थ्रेड-1 I/O ऑपरेशन का इंतज़ार कर रहा है। इस समय, यह GIL जारी करेगा, और थ्रेड-2 इसे प्राप्त करेगा।
- I/O ऑप्स पूरा करने के बाद, यदि थ्रेड-1 अब निष्पादित करना चाहता है, तो उसे फिर से थ्रेड-2 द्वारा GIL जारी किए जाने की प्रतीक्षा करनी होगी।
इसके कारण, किसी भी समय केवल एक थ्रेड ही इंटरप्रेटर तक पहुंच सकता है, जिसका अर्थ है कि किसी निश्चित समय पर केवल एक ही थ्रेड पायथन कोड निष्पादित करेगा।
सिंगल-कोर प्रोसेसर में यह ठीक है क्योंकि यह थ्रेड्स को संभालने के लिए टाइम स्लाइसिंग (इस ट्यूटोरियल का पहला भाग देखें) का उपयोग करेगा। हालाँकि, मल्टी-कोर प्रोसेसर के मामले में, कई थ्रेड्स पर निष्पादित होने वाले CPU-बाउंड फ़ंक्शन का प्रोग्राम की दक्षता पर काफी प्रभाव पड़ेगा क्योंकि यह वास्तव में एक ही समय में सभी उपलब्ध कोर का उपयोग नहीं करेगा।
जी.आई.एल. की आवश्यकता क्यों पड़ी?
सीPython कचरा संग्रहकर्ता एक कुशल मेमोरी प्रबंधन तकनीक का उपयोग करता है जिसे संदर्भ गणना के रूप में जाना जाता है। यह इस प्रकार काम करता है: पायथन में प्रत्येक ऑब्जेक्ट में एक संदर्भ गणना होती है, जो तब बढ़ जाती है जब इसे किसी नए चर नाम को सौंपा जाता है या कंटेनर (जैसे टपल, सूचियाँ, आदि) में जोड़ा जाता है। इसी तरह, जब संदर्भ दायरे से बाहर चला जाता है या जब डेल स्टेटमेंट को कॉल किया जाता है तो संदर्भ गणना कम हो जाती है। जब किसी ऑब्जेक्ट की संदर्भ गणना 0 पर पहुँच जाती है, तो उसे कचरा संग्रह किया जाता है, और आवंटित मेमोरी को मुक्त कर दिया जाता है।
लेकिन समस्या यह है कि संदर्भ गणना चर किसी भी अन्य वैश्विक चर की तरह रेस स्थितियों के लिए प्रवण है। इस समस्या को हल करने के लिए, पायथन के डेवलपर्स ने वैश्विक इंटरप्रेटर लॉक का उपयोग करने का निर्णय लिया। दूसरा विकल्प प्रत्येक ऑब्जेक्ट में एक लॉक जोड़ना था, जिसके परिणामस्वरूप डेडलॉक होता और acquire() और release() कॉल से ओवरहेड बढ़ जाता।
इसलिए, GIL मल्टीथ्रेडेड पायथन प्रोग्राम के लिए एक महत्वपूर्ण प्रतिबंध है जो भारी CPU-बाउंड ऑपरेशन चलाते हैं (प्रभावी रूप से उन्हें सिंगल-थ्रेडेड बनाते हैं)। यदि आप अपने एप्लिकेशन में कई CPU कोर का उपयोग करना चाहते हैं, तो इसका उपयोग करें बहु इसके बजाय मॉड्यूल।
सारांश
- Python मल्टीथ्रेडिंग के लिए 2 मॉड्यूल का समर्थन करता है:
- __धागा मॉड्यूल: यह थ्रेडिंग के लिए निम्न-स्तरीय कार्यान्वयन प्रदान करता है और अप्रचलित है।
- थ्रेडिंग मॉड्यूलयह मल्टीथ्रेडिंग के लिए उच्च-स्तरीय कार्यान्वयन प्रदान करता है और वर्तमान मानक है।
- थ्रेडिंग मॉड्यूल का उपयोग करके थ्रेड बनाने के लिए, आपको निम्नलिखित कार्य करना होगा:
- एक ऐसा वर्ग बनाएं जो विस्तारित हो धागा वर्ग.
- इसके कन्स्ट्रक्टर (__init__) को ओवरराइड करें.
- इसे ओवरराइड करें Daud() विधि.
- इस क्लास का एक ऑब्जेक्ट बनाएं.
- किसी थ्रेड को कॉल करके निष्पादित किया जा सकता है शुरू() विधि.
- RSI में शामिल होने के () विधि का उपयोग अन्य थ्रेड्स को ब्लॉक करने के लिए किया जा सकता है जब तक कि यह थ्रेड (जिस पर जॉइन को कॉल किया गया था) निष्पादन समाप्त नहीं कर देता।
- रेस स्थिति तब उत्पन्न होती है जब एकाधिक थ्रेड एक ही समय में किसी साझा संसाधन तक पहुंचते हैं या उसे संशोधित करते हैं।
- इससे बचा जा सकता है Syncह्रोनाइज़िंग धागे.
- Python थ्रेड्स को सिंक्रनाइज़ करने के 6 तरीकों का समर्थन करता है:
- ताले
- आरलॉक्स
- Semaphores
- स्थितियां
- घटनाएँ, और
- बाधाओं
- लॉक केवल उस विशेष धागे को ही महत्वपूर्ण भाग में प्रवेश करने की अनुमति देता है जिसने लॉक प्राप्त कर लिया है।
- लॉक की 2 प्राथमिक विधियाँ हैं:
- अधिग्रहण करना(): यह लॉक स्थिति को सेट करता है बंद कर दिया। यदि इसे किसी लॉक्ड ऑब्जेक्ट पर कॉल किया जाता है, तो यह तब तक ब्लॉक रहता है जब तक कि संसाधन मुक्त न हो जाए।
- मुक्त करना(): यह लॉक स्थिति को सेट करता है खुला और रिटर्न करता है। यदि अनलॉक ऑब्जेक्ट पर कॉल किया जाता है, तो यह गलत रिटर्न करता है।
- ग्लोबल इंटरप्रेटर लॉक एक ऐसा तंत्र है जिसके माध्यम से केवल 1 सीPython एक समय में 100 से अधिक इंटरप्रेटर प्रक्रिया निष्पादित हो सकती है।
- इसका उपयोग सी की संदर्भ गणना कार्यक्षमता को सुविधाजनक बनाने के लिए किया गया थाPythons का कचरा संग्रहकर्ता।
- बनाने के लिए Python भारी CPU-बाउंड ऑपरेशन वाले ऐप्स के लिए, आपको मल्टीप्रोसेसिंग मॉड्यूल का उपयोग करना चाहिए।