मुख्य सामग्री पर जाएँ

Python Global Interpreter Lock ट्यूटोरियल

Global Interpreter Lock क्या है, यह कैसे काम करता है, और आपको इसका उपयोग क्यों करना चाहिए, जानें।
· 8 मि॰ पढ़ें

AI के साथ खोजें

यदि आप अभी-अभी Python शुरू कर रहे हैं और अधिक सीखना चाहते हैं, तो DataCamp का Introduction to Data Science in Python कोर्स लें।

ग्लोबल इंटरप्रेटर लॉक (GIL) एक ऐसा तंत्र है जो इंटरप्रेटर पर एक वैश्विक लॉक लागू करता है। यह कंप्यूटर-भाषा के इंटरप्रेटरों में थ्रेड्स के निष्पादन को समकालिक करने और प्रबंधित करने के लिए उपयोग होता है ताकि एक समय में केवल एक नेटिव थ्रेड (जो ऑपरेटिंग सिस्टम द्वारा शेड्यूल किया गया हो) ही निष्पादित हो सके।

ऐसी स्थिति में जहाँ आपके पास कई थ्रेड्स हों, यह हो सकता है कि दोनों थ्रेड एक ही समय पर मेमोरी प्राप्त करने का प्रयास करें और परिणामस्वरूप वे मेमोरी में डेटा को ओवरराइट कर दें। इसलिए, इस तरह की स्थिति को रोकने के लिए एक तंत्र की आवश्यकता होती है।

कुछ लोकप्रिय इंटरप्रेटर जिनमें GIL होता है, वे हैं CPython और Ruby MRI। जैसा कि आपमें से अधिकतर जानते हैं, Python एक इंटरप्रेटेड भाषा है; इसकी कई डिस्ट्रीब्यूशन्स हैं जैसे CPython, Jython, IronPython। इनमें से GIL केवल CPython में समर्थित है, और यही Python का सबसे व्यापक रूप से उपयोग किया जाने वाला इम्प्लीमेंटेशन भी है। CPython को C और Python दोनों भाषाओं में विकसित किया गया है, मुख्यतः उन अनुप्रयोगों के साथ काम करने के लिए जिनके भीतर काफी C कोड होता है।

भले ही आपके प्रोसेसर में कई कोर हों, ग्लोबल इंटरप्रेटर एक समय में केवल एक थ्रेड को ही चलने देगा। ऐसा इसलिए है क्योंकि जब कोई थ्रेड चलना शुरू करता है, तो वह ग्लोबल इंटरप्रेटर लॉक हासिल कर लेता है। जब वह किसी I/O ऑपरेशन (डिस्क से/पर डेटा पढ़ना/लिखना) या CPU-बाउंड ऑपरेशन (वेक्टर/मैट्रिक्स गुणन) की प्रतीक्षा करता है, तो वह लॉक छोड़ देता है ताकि उसी प्रोसेस के अन्य थ्रेड्स चल सकें। इस प्रकार, यह आपको अन्य थ्रेड्स को एक ही समय पर चलाने से रोकता है।

thread

आइए ऊपर दिए गए आरेख को समझते हैं। जैसा कि आप देख सकते हैं, एक factorial फ़ंक्शन है और दो थ्रेड 1 और 2; थ्रेड 1 लॉक्ड स्थिति में है जबकि थ्रेड 2 वेट स्थिति में है। इसका मतलब है कि केवल एक थ्रेड ही फ़ंक्शन तक पहुँच पा रहा है। अब, मान लें कि factorial फ़ंक्शन को पूरा होने में 2 सेकंड लगते हैं। तब आदर्श स्थिति में दोनों थ्रेड्स को 2 सेकंड में निष्पादन पूरा कर लेना चाहिए। हालांकि, Python में ऐसा नहीं होता—दोनों थ्रेड्स एक-दूसरे के समानांतर नहीं, बल्कि क्रमिक रूप से चलते हैं।

factorial फ़ंक्शन को कॉल करने वाले थ्रेड 1 और 2 को उतना ही समय लग सकता है जितना एक एकल थ्रेड को उस फ़ंक्शन को दो बार चलाने में लगता है। यह यह भी दर्शाता है कि इंटरप्रेटर द्वारा नियंत्रित मेमोरी मैनेजर thread-safe नहीं है, यानी कई थ्रेड्स एक ही साझा डेटा तक एक साथ पहुँचने में विफल रहते हैं।

इसलिए, GIL (स्रोत: Understanding the Python GIL):

  • थ्रेडिंग ऑपरेशन को सीमित करता है।

  • समानांतर निष्पादन प्रतिबंधित रहता है।

  • GIL सुनिश्चित करता है कि एक समय में इंटरप्रेटर में केवल एक थ्रेड चले।

  • यह मेमोरी प्रबंधन जैसे विभिन्न लो-लेवल विवरणों को सरल बनाने में मदद करता है।

GIL के साथ समानांतर कंप्यूटिंग के स्थान पर cooperative कंप्यूटिंग या समन्वित मल्टीटास्किंग प्राप्त होती है।

gil
Source</a

ऊपर दिए गए आरेख से देखा जा सकता है कि तीन थ्रेड्स हैं। प्रारंभ में थ्रेड 1 चल रहा है और उसने GIL हासिल किया हुआ है, और जब कोई I/O ऑपरेशन जैसे पढ़ना, लिखना आदि होता है, तो थ्रेड 1 GIL छोड़ देता है और फिर थ्रेड 2 उसे हासिल कर लेता है। यह चक्र चलता रहता है, और थ्रेड्स के बीच GIL बारी-बारी से बदलता रहता है जब तक कि सभी थ्रेड्स प्रोग्राम का निष्पादन पूरा नहीं कर लेते। ध्यान रखें कि जिन थ्रेड्स के पास लॉक नहीं है और जिनका निष्पादन पूरा नहीं हुआ है, वे वेटिंग स्टेट में रहेंगे।

Python2 बनाम Python3 में GIL
gil

Python 2.7 में ग्लोबल इंटरप्रेटर लॉक का व्यवहार Python3 की तुलना में अलग है। शुद्ध CPU-बाउंड ऑपरेशन में, क्योंकि कोई I/O ऑपरेशन नहीं होता, थ्रेड लगातार चलता रहता है, और अन्य थ्रेड्स निष्क्रिय या वेट स्थिति में रहते हैं—जो आप नहीं चाहेंगे। इस समस्या को हल करने के लिए, Python2 में ticks नामक अवधारणा है। ग्लोबल इंटरप्रेटर लॉक थ्रेड की स्थिति—जैसे वेट स्टेट, I/O ऑपरेशन, या रनिंग—की जाँच करता है। हालांकि, GIL हर क्षण थ्रेड्स की निगरानी नहीं करता; बल्कि यह ticks की अवधारणा का उपयोग करता है और हर 100 टिक पर थ्रेड्स की जाँच करता है।

एक टिक एक बाइट-कोड निर्देश होता है, और जब 100 बाइट-कोड निर्देश पूरे हो जाते हैं, तो GIL जाँचता है कि थ्रेड्स चल रहे हैं या वेटिंग स्टेट में हैं। याद रखें कि टिक समय से संबंधित नहीं है क्योंकि यह बाइट-कोड निर्देश है। प्रत्येक टिक को चलने में दूसरे की तुलना में अधिक या कम समय लग सकता है।

gil
Source</a

हर 100 टिक के बाद थ्रेड्स की यह आवधिक जाँच विशेषकर CPU-बाउंड ऑपरेशनों में आवश्यक है क्योंकि उनमें कोई I/O ऑपरेशन नहीं होता। ध्यान दें कि आप Python के बिल्ट-इन sys मॉड्यूल का उपयोग करके टिक काउंटर को संशोधित कर सकते हैं।

gil
Source</a

आइए ऊपर दिए गए दृष्टिकोण की कुछ सीमाएँ चर्चा करें:

अब, यदि आप इस आरेख को देखें, तो आप देख सकते हैं कि थ्रेड्स एक-दूसरे के समानांतर नहीं बल्कि क्रम में चल रहे हैं, जिसे ग्लोबल इंटरप्रेटर लॉक द्वारा नियंत्रित cooperative computing भी कहा जाता है। साथ ही, थ्रेड्स को अधिक समय तक प्रतीक्षा करनी पड़ती है। उदाहरण के लिए, थ्रेड T3 को GIL के रिलीज़ होने के लिए T1 और T2 दोनों का इंतज़ार करना पड़ा। इस प्रकार, थ्रेड्स लॉक और कम्प्यूट के लिए भूखे रहते हैं।

एक और कमी यह है कि थ्रेड्स के बीच यह प्रतिस्पर्धा रहती है कि GIL किसे मिलेगा। प्रारंभ में प्राथमिकता के आधार पर T1 को GIL मिला हो सकता है, फिर T2, और आगे। लेकिन जब T3 GIL छोड़ता है, तो वह अन्य सभी थ्रेड्स को सूचना भेजता है, और तब सभी थ्रेड्स GIL हासिल करने के लिए प्रतिस्पर्धा करते हैं। हालांकि, आप प्रत्येक थ्रेड की प्राथमिकताएँ सेट करके इसे कुछ हद तक संभाल सकते हैं।

Python3 में, प्रत्येक थ्रेड के लिए निश्चित समय आबंटित होता है, यानी 5ms का निष्पादन समय। इससे संसाधनों के लिए थ्रेड्स की भूख समाप्त होती है क्योंकि सभी थ्रेड्स के लिए प्रतीक्षा समय समान होता है। थ्रेड्स यह देखने के लिए प्रतीक्षा करेंगे कि क्या थ्रेड 1 I/O या स्लीप ऑपरेशन के कारण जानबूझकर GIL छोड़ता है। यदि नहीं, तो 5ms के बाद इसे छोड़ने के लिए मजबूर किया जाएगा। परिणामस्वरूप, CPU समय के लिए कोई भूख नहीं रहेगी क्योंकि हर थ्रेड को बराबर समय मिलेगा, और GIL हासिल करने की चुनौती कम हो जाएगी।

gil

हालाँकि, यह ध्यान रखना महत्वपूर्ण है कि GIL के नए इम्प्लीमेंटेशन में I/O ऑपरेशनों की तुलना में CPU-बाउंड जॉब्स को अधिक प्राथमिकता दी जाती है। ऐसी स्थिति में जहाँ एक थ्रेड ने GIL छोड़ दिया है और दो थ्रेड प्रतीक्षा कर रहे हैं, GIL उस थ्रेड द्वारा हासिल किया जाएगा जिसे CPU-बाउंड जॉब चलाने की आवश्यकता है, दूसरे की तुलना में।

निष्कर्ष

ट्यूटोरियल पूरा करने पर बधाई।

यह ट्यूटोरियल एक उन्नत विषय को कवर करता है, और उद्देश्य था कि आपको Python में GIL कैसे काम करता है इसकी सैद्धांतिक समझ दी जाए। चूँकि इस ट्यूटोरियल में GIL और थ्रेड्स के कोडिंग पहलुओं को शामिल नहीं किया गया, आपके लिए एक अच्छा अभ्यास यह होगा कि आप स्वयं कई थ्रेड्स और एक ऐसा मेथड बनाकर प्रयोग करें जो I/O-इंटेंसिव कार्य करता हो, और अंत में विश्लेषण करें कि प्रत्येक थ्रेड के बीच GIL कैसे स्विच होता है।

कृपया इस ट्यूटोरियल से संबंधित कोई भी प्रश्न नीचे टिप्पणी अनुभाग में पूछें।

संदर्भ:

यदि आप Python में अभी शुरुआत कर रहे हैं और अधिक सीखना चाहते हैं, तो DataCamp का Introduction to Data Science in Python कोर्स लें।

विषय