पायथन में "थ्रेड लोकल स्टोरेज" क्या है, और मुझे इसकी आवश्यकता क्यों है?


100

पायथन में विशेष रूप से, थ्रेड्स के बीच चर कैसे साझा किए जाते हैं?

हालाँकि मैंने इसका उपयोग किया है threading.Threadइससे पहले कि मैं वास्तव में कभी समझ नहीं पाया या देखा कि कैसे चर साझा किए गए। क्या उन्हें मुख्य धागे और बच्चों के बीच या केवल बच्चों के बीच साझा किया जाता है? इस साझाकरण से बचने के लिए मुझे थ्रेड लोकल स्टोरेज का उपयोग कब करना होगा?

मैंने ताले का उपयोग करके थ्रेड्स के बीच साझा डेटा तक पहुंच को सिंक्रनाइज़ करने के बारे में कई चेतावनियां देखी हैं, लेकिन मुझे अभी तक समस्या का एक अच्छा उदाहरण दिखाई नहीं देता है।

अग्रिम में धन्यवाद!


2
शीर्षक प्रश्न से मेल नहीं खाता है। सवाल धागे के बीच चर साझा करने के साथ है, शीर्षक का अर्थ है कि यह विशेष रूप से थ्रेड स्थानीय भंडारण के बारे में है
केसबेश

2
@ कैशेबश: इस सवाल की ध्वनि से, माइक ने पढ़ा कि साझा डेटा के कारण होने वाली समस्याओं से बचने के लिए टीएलएस आवश्यक है, लेकिन यह स्पष्ट नहीं था कि डेटा को डिफ़ॉल्ट रूप से किसके साथ साझा किया गया था, और इसे कैसे साझा किया गया था। मैंने सवाल को बेहतर मिलान करने के लिए शीर्षक समायोजित किया है।
शोग

जवाबों:


83

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

यदि आप वास्तविक थ्रेड-लोकल स्टोरेज चाहते हैं, तो यह वह जगह threading.localहै जहाँ threading.localधागे के बीच साझा नहीं किए जाते हैं। प्रत्येक थ्रेड केवल उन विशेषताओं को देखता है जो इसे वहां रखा गया है। यदि आप इसके कार्यान्वयन के बारे में उत्सुक हैं, तो स्रोत मानक पुस्तकालय में _threading_local.py में है।


1
क्या आप निम्नलिखित वाक्य के बारे में अधिक जानकारी दे सकते हैं? "यदि आप एटमेटिक रूप से कुछ भी संशोधित करना चाहते हैं, जो आपने केवल इसी बहुत से धागे में नहीं बनाया है, और कहीं भी स्टोर नहीं किया है तो एक और धागा इसे प्राप्त कर सकता है, आपको इसे एक लॉक द्वारा सुरक्षित करना होगा।"
चंगेजिआंग

@changyuheng: यहां परमाणु कार्यों के बारे में एक व्याख्या दी गई है: cs.nott.ac.uk/~psznza/G52CON/lecture4.pdf
टॉम

1
@TomBusby: यदि कोई अन्य धागा इस पर नहीं मिल सकता है, तो हमें इसे लॉक द्वारा संरक्षित करने की आवश्यकता क्यों है, अर्थात हमें प्रक्रिया को परमाणु बनाने की आवश्यकता क्यों है?
चांग्येंग फेंग

2
कृपया आप इसका त्वरित उदाहरण दे सकते हैं: "वस्तुएं हमेशा वैश्विक होती हैं, और कुछ भी उन्हें संदर्भित कर सकता है"। संदर्भ मानकर आप पढ़ते हैं और असाइन / एपेंड नहीं करते हैं?
चर

@ सक्षम: मुझे लगता है कि उनका मतलब है कि मानों में कोई गुंजाइश नहीं है
user1071847

75

निम्नलिखित कोड पर विचार करें:

#/usr/bin/env python

from time import sleep
from random import random
from threading import Thread, local

data = local()

def bar():
    print("I'm called from", data.v)

def foo():
    bar()

class T(Thread):
    def run(self):
        sleep(random())
        data.v = self.getName()   # Thread-1 and Thread-2 accordingly
        sleep(1)
        foo()
>> टी ()। प्रारंभ (); टी ()। शुरू ()
मुझे थ्रेड -2 से बुलाया जाता है
मुझे थ्रेड -1 से बुलाया जाता है 

यहाँ थ्रेडिंग .local () का उपयोग फु ट () के इंटरफ़ेस को बदले बिना रन () से बार () तक कुछ डेटा पास करने के लिए एक त्वरित और गंदे तरीके के रूप में किया जाता है।

ध्यान दें कि वैश्विक चर का उपयोग करने से चाल नहीं चलेगी:

#/usr/bin/env python

from time import sleep
from random import random
from threading import Thread

def bar():
    global v
    print("I'm called from", v)

def foo():
    bar()

class T(Thread):
    def run(self):
        global v
        sleep(random())
        v = self.getName()   # Thread-1 and Thread-2 accordingly
        sleep(1)
        foo()
>> टी ()। प्रारंभ (); टी ()। शुरू ()
मुझे थ्रेड -2 से बुलाया जाता है
मुझे थ्रेड -2 से बुलाया जाता है 

इस बीच, अगर आप फू के तर्क के रूप में इस डेटा को पारित कर सकते हैं () - यह एक अधिक सुंदर और अच्छी तरह से डिज़ाइन किया गया तरीका होगा:

from threading import Thread

def bar(v):
    print("I'm called from", v)

def foo(v):
    bar(v)

class T(Thread):
    def run(self):
        foo(self.getName())

लेकिन यह हमेशा संभव नहीं होता है जब तृतीय-पक्ष या खराब डिज़ाइन किए गए कोड का उपयोग किया जाता है।


18

आप थ्रेड स्थानीय संग्रहण का उपयोग करके बना सकते हैं threading.local()

>>> tls = threading.local()
>>> tls.x = 4 
>>> tls.x
4

टिल्स को संग्रहीत डेटा प्रत्येक थ्रेड के लिए अद्वितीय होगा जो यह सुनिश्चित करने में मदद करेगा कि अनजाने में साझा नहीं होता है।


2

हर दूसरी भाषा की तरह, पायथन में भी हर धागे की एक ही चर पर पहुँच है। 'मुख्य सूत्र' और बाल सूत्र के बीच कोई अंतर नहीं है।

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


0

मैं यहां गलत हो सकता हूं। यदि आप जानते हैं अन्यथा कृपया विस्तार से बताएं क्योंकि इससे यह समझाने में मदद मिलेगी कि किसी को थ्रेड स्थानीय () का उपयोग करने की आवश्यकता क्यों होगी।

यह कथन गलत लगता है, गलत नहीं है: "यदि आप किसी भी चीज़ को परमाणु रूप से संशोधित करना चाहते हैं जो दूसरे धागे तक पहुंच है, तो आपको इसे लॉक के साथ सुरक्षित रखना होगा।" मुझे लगता है कि यह कथन है -> प्रभावी रूप से <- सही है लेकिन पूरी तरह से सही नहीं है। मुझे लगा कि "परमाणु" शब्द का अर्थ है कि पायथन इंटरप्रेटर ने एक बाइट-कोड चंक बनाया है जो सीपीयू के लिए एक बाधा संकेत के लिए कोई जगह नहीं छोड़ता है।

मैंने सोचा था कि परमाणु संचालन पायथन बाइट कोड के हिस्सा हैं जो बीच में आने की अनुमति नहीं देते हैं। पायथन के कथन "रनिंग = ट्रू" परमाणु है। आपको इस मामले में हस्तक्षेप (मुझे विश्वास है) से सीपीयू को लॉक करने की आवश्यकता नहीं है। पायथन बाइट कोड ब्रेकडाउन थ्रेड रुकावट से सुरक्षित है।

पाइथन कोड जैसे "थ्रेड्स_निगम [5] = ट्रू" परमाणु नहीं है। यहां पायथन बाइट कोड के दो भाग हैं; एक वस्तु के लिए सूची () को डी-रेफरेंस और एक वस्तु के लिए एक अन्य बाइट कोड चंक करने के लिए, इस मामले में एक सूची में "स्थान"। एक बाधा को उठाया जा सकता है -> बीच <- दो बाइट-कोड -> चंक्स <-। यह खराब चीजें होती हैं।

थ्रेड लोकल () "परमाणु" से कैसे संबंधित है? यही कारण है कि बयान मुझे गलत लगता है। अगर नहीं समझा सकते तो?


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