2009-08-21 6 views
22

में थ्रेड के साथ एक वैश्विक शब्दकोश का उपयोग करना शब्दकोश मूल्यों को थ्रेड-सुरक्षित एक्सेस/बदल रहा है?पायथन

मैं एक वैश्विक शब्दकोश foo और आईडी id1, id2, ..., idn साथ एक से अधिक थ्रेड की है। क्या foo के मानों को एक्सेस करने और बदलने के लिए यह ठीक है, यदि यह ज्ञात है कि प्रत्येक थ्रेड केवल आईडी-संबंधित मान के साथ काम करेगा, तो id1 के साथ थ्रेड केवल foo[id1] के साथ काम करेगा?

+0

आप ** ** सीपीथन का उपयोग कर रहे हैं, है ना? – voyager

+0

@ वाहर: हाँ, मैं सीपीथन का उपयोग कर रहा हूं। – Alex

उत्तर

34

मान लीजिए CPython: हाँ और नहीं। यह वास्तव में एक साझा शब्दकोश से मूल्यों को लाने/संग्रहीत करने के लिए सुरक्षित है कि कई समवर्ती पढ़ने/लिखने के अनुरोध शब्दकोश को दूषित नहीं करेंगे। यह कार्यान्वयन द्वारा बनाए गए वैश्विक दुभाषिया ताला ("जीआईएल") के कारण है। यही कारण है:

थ्रेड एक चल रहा है:

a = global_dict["foo"] 

थ्रेड बी चल:

global_dict["bar"] = "hello" 

थ्रेड सी चल रहा है:

global_dict["baz"] = "world" 

होगा भ्रष्ट नहीं शब्दकोश, भले ही सभी "पहुंच" समय पर तीन पहुंच प्रयास होते हैं। दुभाषिया उन्हें कुछ अपरिभाषित तरीके से क्रमबद्ध करेगा।

हालांकि, निम्न क्रम के परिणाम अपरिभाषित है:

थ्रेड एक:

if "foo" not in global_dict: 
    global_dict["foo"] = 1 

थ्रेड बी:

global_dict["foo"] = 2 

परीक्षा/धागा एक में सेट के रूप में परमाणु नहीं है ("टाइम-ऑफ-चेक/टाइम-ऑफ-यूज" रेस हालत)।इसलिए, यह आमतौर पर सबसे अच्छा है, अगर आप चीजों को लॉक कर दें:

lock = RLock() 

def thread_A(): 
    lock.acquire() 
    try: 
     if "foo" not in global_dict: 
      global_dict["foo"] = 1 
    finally: 
     lock.release() 

def thread_B(): 
    lock.acquire() 
    try: 
     global_dict["foo"] = 2 
    finally: 
     lock.release() 
+0

'rdread ए' में 'global_dict.setdefault (" foo ", 1)' लॉक अनावश्यक की आवश्यकता होगी? – Claudiu

+0

क्या मैं इसे सही ढंग से समझ रहा हूं। जब तक मैं संशोधन के बिना शब्दकोश में जोड़ रहा हूं, यह सुरक्षित है। यानी थ्रेड में '[ए'] = 1 और एक थ्रेड ['बी'] = 2 थ्रेड बी में ठीक है क्योंकि कुंजी ए और बी समान नहीं हैं? – Cripto

+0

@ user1048138 - नहीं। सुरक्षित क्या है और आपके एप्लिकेशन पर क्या निर्भर नहीं है। एक वर्ग के बारे में सोचें, जिसमें फ़ील्ड 'ए' और 'बी' और आविष्कार है, कि उनमें से एक फ़ील्ड' कोई नहीं 'है और दूसरा' कोई नहीं 'है। जब तक पहुंच ठीक तरह से अनलॉक नहीं होती है, तब तक 'कोई नहीं [नहीं] कोई भी यादृच्छिक संयोजन नहीं है और' बी [नहीं] कोई नहीं है 'इनवेंरिएंट के स्पष्ट उल्लंघन में देखा जा सकता है, अगर केवल "बेवकूफ" गेटर/सेटर का उपयोग किया जाता है (सोचें : 'def set_a (self, a): self.a = a; self.b = कोई नहीं अगर कोई नहीं है self.b' - एक समवर्ती धागा निष्पादन के दौरान अवैध राज्यों का निरीक्षण कर सकता है) – Dirk

3

GIL इसका ख्याल रखता है, यदि आप CPython का उपयोग कर रहे हैं।

वैश्विक दुभाषिया ताला

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

are-locks-unnecessary-in-multi-threaded-python-code-because-of-the-gil देखें।

+0

हालांकि केवल सीपीथॉन से संबंधित है। –

+0

जब तक वह ज्योथन या आयरनपीथन का उपयोग नहीं करता है। – voyager

+0

@ बास्टियन लियोनार्ड: मुझे इसे मारो :) – voyager

20

सबसे अच्छा, सबसे सुरक्षित, पोर्टेबल स्वतंत्र डेटा के साथ प्रत्येक धागा काम है करने के लिए तरीका है:

import threading 
tloc = threading.local() 

अब प्रत्येक धागा एक पूरी तरह से साथ काम करता है स्वतंत्र tloc ऑब्जेक्ट हालांकि यह वैश्विक नाम है। धागा tloc पर विशेषताओं को सेट और सेट कर सकता है, tloc.__dict__ का उपयोग करें, यदि इसे विशेष रूप से एक शब्दकोश की आवश्यकता है,

थ्रेड के लिए थ्रेड-स्थानीय संग्रहण थ्रेड के अंत में दूर हो जाता है; धागे को उनके अंतिम परिणाम रिकॉर्ड करने के लिए, उन्हें put उनके परिणामों को Queue.Queue (जो आंतरिक रूप से थ्रेड-सुरक्षित है) के सामान्य उदाहरण में समाप्त करने से पहले उनके परिणाम प्राप्त करें। इसी तरह, थ्रेड शुरू होने पर डेटा को थ्रेड करने के लिए प्रारंभिक मानों पर काम किया जा सकता है, या Queue से लिया जा सकता है।

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

11

चूंकि मुझे कुछ इसी तरह की आवश्यकता है, मैं यहां उतरा। मैं इस छोटे से स्निपेट में अपने जवाब योग:

#!/usr/bin/env python3 

import threading 

class ThreadSafeDict(dict) : 
    def __init__(self, * p_arg, ** n_arg) : 
     dict.__init__(self, * p_arg, ** n_arg) 
     self._lock = threading.Lock() 

    def __enter__(self) : 
     self._lock.acquire() 
     return self 

    def __exit__(self, type, value, traceback) : 
     self._lock.release() 

if __name__ == '__main__' : 

    u = ThreadSafeDict() 
    with u as m : 
     m[1] = 'foo' 
    print(u) 
जैसे

, तो आपको लॉक धारण करने के लिए with निर्माण का उपयोग कर सकते हैं, जबकि में नगण्य अपने dict()

1

यह कैसे काम करता ?:

>>> import dis 
>>> demo = {} 
>>> def set_dict(): 
...  demo['name'] = 'Jatin Kumar' 
... 
>>> dis.dis(set_dict) 
    2   0 LOAD_CONST    1 ('Jatin Kumar') 
       3 LOAD_GLOBAL    0 (demo) 
       6 LOAD_CONST    2 ('name') 
       9 STORE_SUBSCR 
      10 LOAD_CONST    0 (None) 
      13 RETURN_VALUE 

उपर्युक्त निर्देशों में से प्रत्येक को जीआईएल लॉक होल्ड के साथ निष्पादित किया गया है और STORE_SUBSCR निर्देश कुंजीपटल में कुंजी + मान जोड़ी जोड़ता/अपडेट करता है। तो आप देखते हैं कि शब्दकोश अपडेट परमाणु है और इसलिए थ्रेड सुरक्षित है।