2011-08-23 17 views
27

मैं सी # में धागा सुरक्षित गुण पैदा करने के लिए कोशिश कर रहा हूँ और मुझे यकीन है कि मैं सही रास्ते पर हूँ बनाना चाहते यह प्रतीत होता है के रूप में अगर यह यह करने का सही तरीका नहीं है -,थ्रेड सुरक्षित गुण

C# thread safety with get/set

हालांकि, इस लेख अन्यथा सुझाव देने के लिए लगता है

http://www.codeproject.com/KB/cs/Synchronized.aspx

क्या किसी के पास एक और अधिक निश्चित उत्तर है?

संपादित करें:

कारण यह है कि मैं इस संपत्ति के लिए मनुष्य/सेटर क्या करना चाहते है b/c मैं वास्तव में यह जब यह सेट किया गया है एक घटना सक्रिय करना चाहते हैं - तो कोड वास्तव में इस तरह हो सकता है -

public class PLTracker 
{ 

    public PLEvents Events; 

    private readonly object AvgBuyPriceLocker = new object(); 
    private double _AvgBuyPrice; 
    private double AvgBuyPrice 
    { 
     get 
     { 
      lock (AvgBuyPriceLocker) 
      { 
       return _AvgBuyPrice; 
      } 
     } 
     set 
     { 
      lock (AvgBuyPriceLocker) 
      { 
       Events.AvgBuyPriceUpdate(value); 
       _AvgBuyPrice = value; 
      } 
     } 
    } 
} 

public class PLEvents 
{ 
    public delegate void PLUpdateHandler(double Update); 
    public event PLUpdateHandler AvgBuyPriceUpdateListener; 

    public void AvgBuyPriceUpdate(double AvgBuyPrice) 
    { 
     lock (this) 
     { 
      try 
      { 
       if (AvgBuyPriceUpdateListener!= null) 
       { 
        AvgBuyPriceUpdateListener(AvgBuyPrice); 
       } 
       else 
       { 
        throw new Exception("AvgBuyPriceUpdateListener is null"); 
       } 
      } 
      catch (Exception ex) 
      { 
       Console.WriteLine(ex.Message); 
      } 
     } 
    } 
} 

मैं अपने कोड धागा सुरक्षित तो मुझे बताओ अगर मैं पूरी तरह से गलत तरह से इसके बारे में जा रहा हूँ में संकोच न करें बनाने के लिए बहुत नया हूँ!

विल

+1

यह ठीक है। मैं नहीं देखता, आपको ऐसा क्यों लगता है कि यह नहीं है। लिंक्ड एसओ उत्तर यह इंगित नहीं करता है कि ऐसा करने का बुरा विचार है। –

+0

var property = new ConcurrentValue (); संपत्ति। रीडवैल्यू (x => { // इस थ्रेड सुरक्षित मान x } का उपयोग करें); property.WriteValue (() => { // जटिल समय लेने वाली अवरुद्ध गणना वापसी "गणना"; }); } आप संपत्ति मूल्य धारक के रूप में उपयोग करने के लिए एक समवर्ती वैल्यू क्लास बना सकते हैं, विशेष रूप से लिखने और एकाधिक पढ़ने के लिए लॉकिंग कार्यक्षमता के साथ - ऑब्जेक्ट। प्रॉपर्टी। राइट (() => समय लेने वाली गणना) // ऑब्जेक्ट को ब्लॉक करता है। प्रॉपर्टी। रीड (x =>) –

उत्तर

18

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

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

6

पढ़ना और युगल के लेखन परमाणु है वैसे भी ( source) पढ़ने और युगल के लेखन नहीं परमाणु कई प्रकार के लिए पढ़ने है और इसलिए यह एक ताला का उपयोग कर एक डबल करने के लिए उपयोग की रक्षा के लिए आवश्यक हो जाएगा, फिर भी और लेखन है परमाणु और इतने निम्नलिखित बस के रूप में सुरक्षित होगा:

private float AvgBuyPrice 
{ 
    get; 
    set; 
} 

मेरे मुद्दा यह है कि धागा सुरक्षा के लिए बस अपने गुण की रक्षा करने के तुलना में अधिक जटिल है। एक साधारण उदाहरण के लिए मान लीजिए कि मैं दो गुण AvgBuyPrice और StringAvgBuyPrice है:

private string StringAvgBuyPrice { get; set; } 
private float AvgBuyPrice { get; set; } 

और मैं औसत खरीद मूल्य thusly अद्यतन लगता है: में गुण की रक्षा

this.AvgBuyPrice = value; 
this.StringAvgBuyPrice = value.ToString(); 

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

+4

आपके एमएसडीएन स्रोत के मुताबिक, डबल्स * पर * परमाणु होने की गारंटी नहीं है: "लंबे, उलझन, डबल, और दशमलव, साथ ही उपयोगकर्ता द्वारा परिभाषित प्रकार सहित अन्य प्रकार के पढ़ और लिखते हैं, नहीं हैं परमाणु होने की गारंटी है। " – JeremyDWill

+0

@ जेरेमी डीविल अच्छी तरह से देखा - मैंने अपना प्रश्न संपादित किया है, हालांकि मुझे लगता है कि मेरा बिंदु अभी भी खड़ा है। – Justin

+0

मैं जेरेमीडविल के साथ सहमत हूं, आपको यह समझना चाहिए कि डबल 64 बिट नंबर है, इसलिए 32 बिट मशीनों के साथ काम करते समय पढ़ना और लिखना ऑपरेशन एक से अधिक चरणों में किया जा सकता है। –

16

ताले, जैसा कि आपने उन्हें लिखा है, व्यर्थ हैं। उदाहरण के लिए वेरिएबल पढ़ने वाला थ्रेड,

  1. लॉक प्राप्त करें।
  2. मूल्य पढ़ें।
  3. लॉक जारी करें।
  4. किसी भी तरह से पढ़े गए मान का उपयोग करें।

चरण 3 के बाद मान को संशोधित करने से किसी अन्य धागे को रोकने के लिए कुछ भी नहीं है। क्योंकि .NET में चरणीय पहुंच परमाणु है (नीचे चेतावनी देखें), लॉक वास्तव में यहां बहुत अधिक प्राप्त नहीं कर रहा है: केवल एक ओवरहेड जोड़ना। अनलॉक उदाहरण के साथ तुलना:

  1. मान पढ़ें।
  2. किसी भी तरह से पढ़े गए मान का उपयोग करें।

एक और धागा चरण 1 और 2 के बीच मान को बदल सकता है और यह लॉक किए गए उदाहरण से अलग नहीं है।

आप राज्य सुनिश्चित करने के लिए परिवर्तन नहीं होता है जब आप कुछ प्रसंस्करण कर रहे हैं चाहते हैं, आप मूल्य पढ़ सकते हैं और ताला contex भीतर है कि मूल्य का उपयोग कर प्रसंस्करण कार्य करना होगा:

  1. ताला प्राप्त ।
  2. मूल्य पढ़ें।
  3. किसी भी तरह से पढ़े गए मान का उपयोग करें।
  4. लॉक जारी करें।

ऐसा कहकर, ऐसे मामले हैं जब आपको चर का उपयोग करते समय लॉक करने की आवश्यकता होती है। ये आमतौर पर अंतर्निहित प्रोसेसर के कारणों के कारण होते हैं: double चर को 32 बिट मशीन पर एक ही निर्देश के रूप में पढ़ा या लिखा नहीं जा सकता है, उदाहरण के लिए, इसलिए आपको भ्रष्ट मान सुनिश्चित करने के लिए लॉक (या वैकल्पिक रणनीति का उपयोग करना) चाहिए पढ़ें।

10

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

उदाहरण के लिए, छद्म कोड की निम्न बिट संभालने:

void updateAvgBuyPrice() 
{ 
    float oldPrice = AvgBuyPrice; 
    float newPrice = oldPrice + <Some other logic here> 
    //Some more new price calculation here 
    AvgBuyPrice = newPrice; 
} 

इस कोड को एक ही समय में एक से अधिक थ्रेड से कहा जाता है, तो आपके ताला तर्क किसी काम का नहीं है। कल्पना कीजिए कि ए AVGBuyPrice प्राप्त कर रहा है और कुछ गणना कर रहा है। अब यह करने से पहले, थ्रेड बी को AvgBuyPrice भी मिल रहा है और गणना शुरू हो रही है। इस बीच थ्रेड ए किया जाता है और AvgBuyPrice को नया मान असाइन करेगा। हालांकि, कुछ ही क्षण बाद, इसे थ्रेड बी द्वारा ओवरराइट किया जाएगा (जो अभी भी पुराने मान का उपयोग करता है) और थ्रेड ए का काम पूरी तरह से खो गया है।

तो आप इसे कैसे ठीक करते हैं? हम ताले उपयोग करने के लिए थे (जो ugliest और धीरे समाधान होगा, लेकिन सबसे आसान तुम सिर्फ बहु सूत्रण के साथ शुरू कर रहे हैं), हम सब तर्क जो ताले में AvgBuyPrice बदलता है डाल करने के लिए की जरूरत है:

void updateAvgBuyPrice() 
{ 
    lock(AvgBuyPriceLocker) 
    { 
     float oldPrice = AvgBuyPrice; 
     float newPrice = oldPrice + <Some other code here> 
     //Some more new price calculation here 
     AvgBuyPrice = newPrice; 
    } 
} 

अब , अगर थ्रेड बी गणना करना चाहता है, जबकि थ्रेड ए अभी भी व्यस्त है, तो यह तब तक इंतजार करेगा जब तक कि थ्रेड ए नहीं किया जाता है और फिर नए मान का उपयोग करके अपना काम करता है। हालांकि, ध्यान रखें कि कोई अन्य कोड जो AvgBuyPrice को भी संशोधित करता है, उसे काम करते समय AvgBuyPriceLocker को भी लॉक करना चाहिए!

फिर भी, यदि अक्सर उपयोग किया जाता है तो यह धीमा हो जाएगा।ताले महंगे हैं और ताले से बचने के लिए कई अन्य तंत्र हैं, बस लॉक-फ्री एल्गोरिदम की खोज करें।

+3

मार्ट की टिप्पणी में जोड़ने के लिए, जोसेफ अल्बाहारी ने सभी थ्रेडिंग पहलुओं के बारे में [एक उत्कृष्ट ऑनलाइन पुस्तक] (http://www.albahari.com/threading/) लिखा है। पुस्तक एचटीएमएल या पीडीएफ के रूप में उपलब्ध है। –

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