python - जीआईएल की वजह से बहु-थ्रेडेड पायथन कोड में ताले अनावश्यक हैं?




multithreading locking (6)

यदि आप पाइथन के कार्यान्वयन पर भरोसा कर रहे हैं जिसमें ग्लोबल इंटरप्रेटर लॉक (यानी सीपीथन) है और मल्टीथ्रेड कोड लिख रहा है, तो क्या आपको वास्तव में ताले की जरूरत है?

यदि जीआईएल समानांतर में एकाधिक निर्देशों को निष्पादित करने की अनुमति नहीं देता है, तो डेटा साझा करने के लिए अनावश्यक नहीं होगा?

खेद है कि यह एक बेवकूफ सवाल है, लेकिन ऐसा कुछ है जो मैंने हमेशा पाइथन के बारे में बहु-प्रोसेसर / कोर मशीनों पर सोचा है।

एक ही चीज किसी भी अन्य भाषा कार्यान्वयन पर लागू होगी जिसमें जीआईएल है।


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


इस पर इस तरीके से विचार करें:

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

समस्या यह है कि थ्रेड को कहीं भी निलंबित किया जा सकता है, उदाहरण के लिए, यदि मैं बी = (ए + बी) * 3 की गणना करना चाहता हूं, तो यह कुछ इस तरह के निर्देश उत्पन्न कर सकता है:

1    a += b
2    a *= 3
3    b = a

अब, मान लें कि थ्रेड में चल रहा है और उस धागे को लाइन 1 या 2 के बाद निलंबित कर दिया गया है और फिर दूसरा थ्रेड चलता है और चलता है:

b = 5

फिर जब दूसरा धागा फिर से शुरू होता है, बी पुराने गणना वाले मानों द्वारा ओवरराइट किया जाता है, जो शायद अपेक्षित नहीं था।

तो आप देख सकते हैं कि भले ही वे एक ही समय में चल रहे न हों, फिर भी आपको लॉकिंग की आवश्यकता है।


चर्चा में जोड़ना:

चूंकि जीआईएल मौजूद है, कुछ ऑपरेशन पायथन में परमाणु हैं और उन्हें लॉक की आवश्यकता नहीं है।

http://www.python.org/doc/faq/library/#what-kinds-of-global-value-mutation-are-thread-safe

जैसा कि अन्य उत्तरों ने कहा है, हालांकि, जब भी एप्लिकेशन तर्क की आवश्यकता होती है (जैसे किसी निर्माता / उपभोक्ता समस्या में) आपको अभी भी ताले का उपयोग करने की आवश्यकता होती है।


ताले अभी भी जरूरी हैं। मैं यह समझाने की कोशिश करूंगा कि उन्हें क्यों जरूरी है।

दुभाषिया में कोई भी ऑपरेशन / निर्देश निष्पादित किया जाता है। जीआईएल सुनिश्चित करता है कि दुभाषिया को एक विशेष थ्रेड पर एक ही थ्रेड द्वारा आयोजित किया जाता है। और एकाधिक थ्रेड के साथ आपका प्रोग्राम एक दुभाषिया में काम करता है। किसी विशेष समय पर, यह दुभाषिया एक धागे द्वारा आयोजित किया जाता है। इसका मतलब है कि दुभाषिया धारण करने वाला केवल थ्रेड समय के किसी भी समय चल रहा है

मान लीजिए कि दो धागे हैं, टी 1 और टी 2 कहें, और दोनों दो निर्देशों को निष्पादित करना चाहते हैं जो वैश्विक चर के मूल्य को पढ़ रहे हैं और इसे बढ़ा रहे हैं।

#increment value
global var
read_var = var
var = read_var + 1

जैसा कि ऊपर रखा गया है, जीआईएल केवल यह सुनिश्चित करता है कि दो धागे एक साथ निर्देश को निष्पादित नहीं कर सकते हैं, जिसका अर्थ है कि दोनों धागे किसी विशेष समय पर read_var = var निष्पादित नहीं कर सकते हैं। लेकिन वे एक के बाद एक निर्देश को निष्पादित कर सकते हैं और आपको अभी भी समस्या हो सकती है। इस स्थिति पर विचार करें:

  • मान लीजिए read_var 0 है।
  • जीआईएल थ्रेड टी 1 द्वारा आयोजित किया जाता है।
  • t1 read_var = var निष्पादित करता है। तो, t1 में read_var 0 है। जीआईएल केवल यह सुनिश्चित करेगा कि इस पठन ऑपरेशन को इस समय किसी अन्य थ्रेड के लिए निष्पादित नहीं किया जाएगा।
  • जीआईएल थ्रेड टी 2 को दिया जाता है।
  • t2 read_var = var निष्पादित करता है। लेकिन read_var अभी भी 0 है। तो, t2 में read_var 0 है।
  • जीआईएल को टी 1 दिया जाता है।
  • टी 1 निष्पादित var = read_var+1 और var 1 बन जाता है।
  • जीआईएल को टी 2 दिया जाता है।
  • t2 read_var = 0 सोचता है, क्योंकि यही वह पढ़ता है।
  • t2 var = read_var+1 निष्पादित करता है और var 1 बन जाता है।
  • हमारी उम्मीद थी कि var 2 बनना चाहिए।
  • इसलिए, एक लॉक का उपयोग दोनों परमाणु संचालन के रूप में पढ़ने और बढ़ने के लिए किया जाना चाहिए।
  • क्या हैरिस का जवाब एक कोड उदाहरण के माध्यम से बताता है।

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

उदाहरण के लिए:

#!/usr/bin/env python
import threading

shared_balance = 0

class Deposit(threading.Thread):
    def run(self):
        for _ in xrange(1000000):
            global shared_balance
            balance = shared_balance
            balance += 100
            shared_balance = balance

class Withdraw(threading.Thread):
    def run(self):
        for _ in xrange(1000000):
            global shared_balance
            balance = shared_balance
            balance -= 100
            shared_balance = balance

threads = [Deposit(), Withdraw()]

for thread in threads:
    thread.start()

for thread in threads:
    thread.join()

print shared_balance

यहां, साझा कोड को साझा करने के बीच आपका कोड बाधित किया जा सकता है ( balance = shared_balance ) और बदले गए परिणाम को वापस ( shared_balance = balance ) shared_balance = balance , जिससे खोए गए अपडेट का कारण बनता है। नतीजा साझा राज्य के लिए एक यादृच्छिक मूल्य है।

अद्यतनों को सुसंगत बनाने के लिए, रन विधियों को साझा-संशोधित-लिखने वाले अनुभागों (लूप के अंदर) के आस-पास साझा स्थिति को लॉक करने की आवश्यकता होगी या जब यह पढ़ा गया था तब साझा स्थिति बदल गई थी तो पता लगाने का कोई तरीका होगा


यह पोस्ट जीआईएल का काफी उच्च स्तर पर वर्णन करता है:

विशेष रूप से रुचि ये उद्धरण हैं:

प्रत्येक दस निर्देश (यह डिफ़ॉल्ट बदला जा सकता है), कोर वर्तमान धागे के लिए जीआईएल जारी करता है। उस बिंदु पर, ओएस लॉक के लिए प्रतिस्पर्धा करने वाले सभी धागे से एक धागा चुनता है (संभवतः उसी धागे को चुनना जो अभी जीआईएल जारी करता है - आपके पास कोई नियंत्रण नहीं है जिस पर धागा चुना जाता है); वह धागा जीआईएल प्राप्त करता है और फिर एक और दस बाइटकोड के लिए चलाता है।

तथा

ध्यान से ध्यान दें कि जीआईएल केवल शुद्ध पायथन कोड को प्रतिबंधित करता है। एक्सटेंशन (आमतौर पर सी में लिखे गए बाहरी पायथन पुस्तकालयों को लिखा जा सकता है जो लॉक को छोड़ते हैं, जो तब पाइथन दुभाषिया को एक्सटेंशन से अलग से चलाने की अनुमति देता है जब तक एक्सटेंशन लॉक को पुनः प्राप्त नहीं करता है।

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







locking