2012-03-01 15 views
7

मैं एक थ्रेड-सुरक्षित विधि लिखने का प्रयास कर रहा हूं जिसे केवल एक बार (प्रति ऑब्जेक्ट इंस्टेंस) कहा जा सकता है। यदि इसे पहले कहा गया है तो एक अपवाद फेंक दिया जाना चाहिए।कैसे करें: एक थ्रेड-सुरक्षित विधि लिखें जिसे केवल एक बार बुलाया जा सकता है?

मैं दो समाधान के साथ आया हूं। क्या वे दोनों सही हैं? यदि नहीं, तो उनके साथ क्या गलत है?

  1. lock साथ :

    public void Foo() 
    { 
        lock (fooLock) 
        { 
         if (fooCalled) throw new InvalidOperationException(); 
         fooCalled = true; 
        } 
        … 
    } 
    private object fooLock = new object(); 
    private bool fooCalled; 
    
  2. Interlocked.CompareExchange के साथ:

    public void Foo() 
    { 
        if (Interlocked.CompareExchange(ref fooCalled, 1, 0) == 1) 
         throw new InvalidOperationException(); 
        … 
    } 
    private int fooCalled; 
    

    अगर मैं गलत नहीं हूँ, इस समाधान ताला मुक्त होने का लाभ (है जो मेरे मामले में अप्रासंगिक लगता है), और इसके लिए कम निजी क्षेत्रों की आवश्यकता है ।

मैं उचित विचारों के लिए भी खुला हूं कि समाधान को प्राथमिकता दी जानी चाहिए, और यदि कोई बेहतर तरीका है तो आगे के सुझावों के लिए।

उत्तर

5

आपका Interlocked.CompareExchange समाधान सबसे अच्छा लग रहा है, और (के रूप में आपने कहा था) ताला मुक्त है। यह अन्य समाधानों की तुलना में काफी कम जटिल है। ताले काफी हेवीवेट हैं, जबकि CompareExchange को एक सीएएस सीपीयू निर्देश में संकलित किया जा सकता है। मैं कहता हूं कि उस के साथ जाओ।

+0

जिज्ञासा से बाहर: जब आप कहते हैं कि यह "कम जटिल" है, तो आप अंधेरे के पीछे चल रहे सभी चीजों का उल्लेख करते हैं; औसत प्रोग्रामर के लिए 'इंटरलाक्ड। एक्सचेंज कॉम्पारेयर' निर्माण की समझने की आसानी/आसानी से आप कैसे निर्णय लेंगे? – stakx

+1

@stakx: यही टिप्पणियां हैं। जब एक प्रोग्रामर कुछ मुठभेड़ करता है जिसे वे समझ में नहीं आता है, तो उन्हें इसे देखना चाहिए ताकि वे इसे समझ सकें। इस तरह वे एक बेहतर प्रोग्रामर बन जाते हैं। – thecoop

+0

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

0

डबल जाँच ताला गपशप है कि तुम क्या कर रहे हैं के बाद:

class Foo 
{ 
    private object someLock = new object(); 
    private object someFlag = false; 


    void SomeMethod() 
    { 
    // to prevent locking on subsequent calls   
    if(someFlag) 
     throw new Exception(); 

    // to make sure only one thread can change the contents of someFlag    
    lock(someLock) 
    { 
     if(someFlag) 
     throw new Exception(); 

     someFlag = true;      
    } 

    //execute your code 
    } 
} 

सामान्य तौर पर जब ये कोशिश जैसे मुद्दों से अवगत कराया और अच्छी तरह से एक की तरह patters जानते का पालन करें:

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

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

+0

** 1। ** ऐसा लगता है कि 'कुछ फ्लैग' गैर-परमाणु पढ़ने/लिखना प्रतीत होता है। क्या आप वाकई सही हैं? ** 2। ** 'इंटरलाक्ड.कंपारे एक्सचेंज'-आधारित समाधान के बारे में क्या? – stakx

+0

sry सबसे महत्वपूर्ण लाइन – ntziolis

+0

हम्म भूल गई है ... यदि आप अपवाद फेंकने जा रहे हैं तो इस तरह की कोई समस्या लॉक कर रही है, वैसे भी? – stakx

-1
Task task = new Task((Action)(() => { Console.WriteLine("Called!"); })); 
    public void Foo() 
    { 
     task.Start(); 
    } 

    public void Bar() 
    { 
     Foo(); 
     Foo();//this line will throws different exceptions depends on 
       //whether task in progress or task has already been completed 
    }  
+1

यह कहने के लिए खेद है कि यह प्रश्न का उत्तर नहीं देता है। (बीटीडब्लू।, मुझे टास्क समांतर लाइब्रेरी से अवगत है, लेकिन इसे हर जगह इस्तेमाल नहीं किया जा सकता है, उदाहरण के लिए जहां प्रश्न में विधि एक इंटरफेस विधि लागू करती है।) – stakx

+1

आपके मामले के अनुसार तय – pamidur

+0

@pamidur आप एक अलग समस्या को हल करने की कोशिश कर रहे हैं – ntziolis

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