2009-07-02 10 views
14

मैं this question पढ़ रहा था अगर मैं एक वर्ग है कि मायने रखता है (जो आप को पढ़ने के लिए मैं क्योंकि कॉपी करेंगे क्या है नहीं है ... मैं सिर्फ शो आप मेरी प्रेरणा देना चाहता था) ...पायथन थ्रेडसेफ में क्लास वैरिएबल को संशोधित कर रहा है?

तो, कैसे कई मामलों बनाया गया:

class Foo(object): 
    instance_count = 0 
    def __init__(self): 
    Foo.instance_count += 1 

मेरा प्रश्न है, अगर मैं एक से अधिक थ्रेड में फू वस्तुओं को बनाने, instance_count सही होने जा रहा है है? क्या कक्षा चर कई धागे से संशोधित करने के लिए सुरक्षित हैं?

उत्तर

21

यह सीपीथॉन पर भी थ्रेडसेफ नहीं है। खुद के लिए देखने के लिए इस प्रयास करें:

import threading 

class Foo(object): 
    instance_count = 0 

def inc_by(n): 
    for i in xrange(n): 
     Foo.instance_count += 1 

threads = [threading.Thread(target=inc_by, args=(100000,)) for thread_nr in xrange(100)] 
for thread in threads: thread.start() 
for thread in threads: thread.join() 

print(Foo.instance_count) # Expected 10M for threadsafe ops, I get around 5M 

कारण यह है कि जबकि INPLACE_ADD जीआईएल के तहत परमाणु है, विशेषता अभी भी भरा हुआ है और दुकान है (देखें dis.dis (फू .__ init__))। वर्ग चर के लिए उपयोग क्रमानुसार करने लॉक का उपयोग करें:

Foo.lock = threading.Lock() 

def interlocked_inc(n): 
    for i in xrange(n): 
     with Foo.lock: 
      Foo.instance_count += 1 

threads = [threading.Thread(target=interlocked_inc, args=(100000,)) for thread_nr in xrange(100)] 
for thread in threads: thread.start() 
for thread in threads: thread.join() 

print(Foo.instance_count) 
+0

मैं आपके दूसरे उदाहरण में विश्वास करता हूं कि आप थ्रेड लक्ष्य को inc_by के बजाय interlocked_inc होना चाहते हैं। – tgray

+0

धन्यवाद, सही। बहुत उदार कॉपी और पेस्ट प्रोग्रामिंग कभी-कभी मेरे साथ पकड़ लेती है। –

+0

धन्यवाद चींटियों Aasma :-)। जैसा कि मुझे संदेह है। मुझे यह साबित करने के लिए धन्यवाद। जैसा कि tgray बताता है, आपका दूसरा लक्ष्य interlocked_inc होना चाहिए। लेकिन एक बार जब आप इसे बदल देते हैं ... दोषपूर्ण दिखता है। – Tom

-4

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

+2

क्या Foo.instance_count + = 1 और काम की परमाणु इकाई है? –

+0

शायद मुझे समझ में नहीं आता कि जीआईएल कैसे काम करता है ... लेकिन मुझे अभी भी यह नहीं दिख रहा है। Thread1 example_count पढ़ नहीं सकता है। फिर थ्रेड 1 बंद हो जाता है। थ्रेड 2 example_count पढ़ता है, फिर बंद हो जाता है। थ्रेड 1 संशोधित करता है और लिखता है। थ्रेड 2 लिखता है। तो आप एक वृद्धि खो देते हैं? जीआईएल कैसे सुनिश्चित करता है कि थ्रेड पूरे + = ऑपरेशन के माध्यम से चलता है? – Tom

+0

हा, मैं मूल रूप से पूछ रहा था कि सैम केसर ने मुझसे पहले क्या पूछा था। – Tom

8

कोई इसे सुरक्षित थ्रेड नहीं है। मुझे कुछ दिन पहले इसी तरह की समस्या का सामना करना पड़ा और मैंने एक सजावटी को लॉक धन्यवाद लागू करने का विकल्प चुना। लाभ यह है कि यह कोड को पठनीय बनाता है:

 
def threadsafe_function(fn): 
    """decorator making sure that the decorated function is thread safe""" 
    lock = threading.Lock() 
    def new(*args, **kwargs): 
     lock.acquire() 
     try: 
      r = fn(*args, **kwargs) 
     except Exception as e: 
      raise e 
     finally: 
      lock.release() 
     return r 
    return new 

class X: 
    var = 0 

    @threadsafe_function  
    def inc_var(self): 
     X.var += 1  
     return X.var 
+0

ऑफ़-विषय, लेकिन क्या आप अपवाद हैंडलर के बाद दो लॉक.release() कॉल को "else:" अनुभाग में हटा सकते हैं? –

+0

क्या आप अंततः खंड में हैं? एक अपवाद उठाए जाने पर किसी और में ऐसा नहीं किया जाएगा – luc

+0

आह हाँ, यही मेरा मतलब था। धन्यवाद! –

संबंधित मुद्दे