2009-09-04 20 views
25

मैं है कोड का निम्न भाग:सी # थ्रेड-सुरक्षित में उपज वापसी है?

private Dictionary<object, object> items = new Dictionary<object, object>; 
public IEnumerable<object> Keys 
{ 
    get 
    { 
     foreach (object key in items.Keys) 
     { 
      yield return key; 
     } 
    } 
} 

इस सूत्र के लिए सुरक्षित है? यदि नहीं, तो मुझे लूप के आसपास lock या yield return रखना होगा?

Thread1 Keys संपत्ति तक पहुँचता Thread2 अंतर्निहित शब्दकोश में कोई आइटम जोड़ता है, जबकि:

यहाँ मैं क्या मतलब है। थ्रेड 1 थ्रेड 2 के जोड़ से प्रभावित है?

+0

आपकी दूसरी उदाहरण के लिए, लॉक हो जाएगा वापसी एक गणनीय तो अनलॉक। और अनलॉक के बाद आप फिर से शुरू करेंगे। – Guillaume

+0

ओह, आप सही हैं। मैंने अभी देखा है कि यह एक बुरा उदाहरण है। मैं सवाल संपादित करूंगा। – Albic

+0

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

उत्तर

7

ठीक है, मैंने कुछ परीक्षण किया और एक दिलचस्प परिणाम प्राप्त किया।

ऐसा लगता है कि यह yield कीवर्ड से अंतर्निहित संग्रह के गणनाकर्ता का एक मुद्दा है। गणनाकर्ता (वास्तव में इसकी MoveNext विधि) InvalidOperationException फेंकता है (यदि सही ढंग से कार्यान्वित किया गया है) तो गणना बदल गई है। MSDN documentation of the MoveNext method के मुताबिक यह अपेक्षित व्यवहार है।

क्योंकि संग्रह के माध्यम से गणना करना आमतौर पर थ्रेड-सुरक्षित नहीं है yield return या तो नहीं है।

20

थ्रेड-सुरक्षित द्वारा आपका क्या मतलब है?

आपको निश्चित रूप से उस शब्द को बदलना नहीं चाहिए जब आप इसे चालू कर रहे हों, चाहे एक ही धागे में हों या नहीं।

यदि शब्दकोश को सामान्य रूप से एकाधिक धागे में एक्सेस किया जा रहा है, तो कॉलर को लॉक (उसी तक पहुंचने वाला एक ही) लेना चाहिए ताकि वे परिणामस्वरूप पुनरावृत्ति की अवधि के लिए लॉक कर सकें।

संपादित करें: अपने संपादन का जवाब देने के लिए, में कोई रास्ता नहीं लॉक कोड से मेल खाता है। एक इटरेटर ब्लॉक द्वारा स्वचालित रूप से कोई लॉक नहीं लिया जाता है - और यह syncRoot के बारे में कैसे पता चलेगा?

इसके अलावा, बस IEnumerable<TKey> की वापसी ताला लगा यह थ्रेड-सुरक्षित नहीं है या तो - जब यह लौटने अनुक्रम, नहीं अवधि के दौरान जो इस पर दोहराया जा रहा है क्योंकि ताला केवल समय की अवधि को प्रभावित करता है। क्या yield कीवर्ड के साथ पर्दे के पीछे क्या होता है पर इस पोस्ट को बाहर

+0

हां, मेरा मतलब सूची के बजाय शब्दकोश था। पहला संपादन गलत था (इसके बारे में खेद है) और मैंने इसे हटा दिया। मैंने प्रश्न के लिए अपेक्षित व्यवहार जोड़ा। वास्तव में उपयोगी लिंक के लिए – Albic

18

की जांच:

Behind the scenes of the C# yield keyword

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

+0

+1। – Albic

3

मुझे विश्वास है कि यह है, लेकिन मुझे कोई संदर्भ नहीं मिल रहा है जो इसकी पुष्टि करता है। हर बार जब किसी भी धागा पुनरावर्तक पर foreach कहता है, एक नया धागा स्थानीय * अंतर्निहित IEnumerator का उदाहरण बनाया जाना चाहिए, इसलिए वहाँ किसी भी "साझा" स्मृति राज्य कि दो धागे पर संघर्ष कर सकते हैं नहीं होना चाहिए ...

  • थ्रेड लोकल - इस अर्थ में कि यह संदर्भ चर उस थ्रेड
+0

हाँ लेकिन क्या होगा अगर GetEnumerator को कॉल करें और फिर IENumerator साझा करें? उसे स्पष्ट करना चाहिए कि वह अपने धागे के साथ क्या कर रहा है। – Guillaume

3

मेरा मानना ​​है कि उपज कार्यान्वयन थ्रेड-सुरक्षित है। दरअसल, आप घर पर उस साधारण कार्यक्रम को चला सकते हैं और आप देखेंगे कि listInt() विधि की स्थिति सही ढंग से सहेजी गई है और प्रत्येक धागे के लिए अन्य धागे से किनारे के प्रभाव के बिना बहाल की गई है।

public class Test 
{ 
    public void Display(int index) 
    { 
     foreach (int i in listInt()) 
     { 
      Console.WriteLine("Thread {0} says: {1}", index, i); 
      Thread.Sleep(1); 
     } 

    } 

    public IEnumerable<int> listInt() 
    { 
     for (int i = 0; i < 5; i++) 
     { 
      yield return i; 
     } 
    } 
} 

class MainApp 
{ 
    static void Main() 
    { 
     Test test = new Test(); 
     for (int i = 0; i < 4; i++) 
     { 
      int x = i; 
      Thread t = new Thread(p => { test.Display(x); }); 
      t.Start(); 
     } 

     // Wait for user 
     Console.ReadKey(); 
    } 
} 
+0

+1। मैंने भी यह सत्यापित किया है कि सी # 4.0 कंपाइलर द्वारा उत्पन्न इटेटरेटर स्टेट मशीन उपज धागे सुरक्षित हैं। –

2
class Program 
{ 
    static SomeCollection _sc = new SomeCollection(); 

    static void Main(string[] args) 
    { 
     // Create one thread that adds entries and 
     // one thread that reads them 
     Thread t1 = new Thread(AddEntries); 
     Thread t2 = new Thread(EnumEntries); 

     t2.Start(_sc); 
     t1.Start(_sc); 
    } 

    static void AddEntries(object state) 
    { 
     SomeCollection sc = (SomeCollection)state; 

     for (int x = 0; x < 20; x++) 
     { 
      Trace.WriteLine("adding"); 
      sc.Add(x); 
      Trace.WriteLine("added"); 
      Thread.Sleep(x * 3); 
     } 
    } 

    static void EnumEntries(object state) 
    { 
     SomeCollection sc = (SomeCollection)state; 
     for (int x = 0; x < 10; x++) 
     { 
      Trace.WriteLine("Loop" + x); 
      foreach (int item in sc.AllValues) 
      { 
       Trace.Write(item + " "); 
      } 
      Thread.Sleep(30); 
      Trace.WriteLine(""); 
     } 
    } 
} 

class SomeCollection 
{ 
    private List<int> _collection = new List<int>(); 
    private object _sync = new object(); 

    public void Add(int i) 
    { 
     lock(_sync) 
     { 
      _collection.Add(i); 
     } 
    } 


    public IEnumerable<int> AllValues 
    { 
     get 
     { 
      lock (_sync) 
      { 
       foreach (int i in _collection) 
       { 
        yield return i; 
       } 
      } 
     } 
    } 
} 
+0

यहां एक साधारण उदाहरण दिखाया गया है कि उपज स्वयं धागा सुरक्षित नहीं है। जैसा लिखा है कि यह थ्रेड सुरक्षित है लेकिन यदि आप AllValues ​​में लॉक (_sync) पर टिप्पणी करते हैं तो आपको यह सत्यापित करने में सक्षम होना चाहिए कि यह कुछ बार चलकर थ्रेड सुरक्षित नहीं है। यदि आपको एक अवैधऑपरेशन अपवाद मिलता है तो यह साबित करता है कि यह थ्रेड सुरक्षित नहीं है। – sjp

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