2012-06-01 16 views
35

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

मेरा प्रश्न तीन गुना है:

मान लीजिए मैं एक सरणी, NSMutableArray *myAwesomeArray;

फोल्ड 1 है:

अब मुझे ठीक कर लें मैं गलत हूँ, लेकिन से मैं क्या समझ , @synchronized(myAwesomeArray){...} का उपयोग करके दो थ्रेड को कोड के उसी ब्लॉक तक पहुंचने से रोक दिया जाएगा।

-(void)doSomething { 
    @synchronized(myAwesomeArray) { 
     //some read/write operation on myAwesomeArray 
    } 
} 

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

फोल्ड 2:

अगर myAwesomeArray अलग अलग तरीकों से एक से अधिक थ्रेड द्वारा पहुँचा जा रहा है तो मैं क्या करूँ? अगर मैं की तरह कुछ है:

- (void)readFromArrayAccessedByThreadOne { 
    //thread 1 reads from myAwesomeArray 
} 

- (void)writeToArrayAccessedByThreadTwo { 
    //thread 2 writes to myAwesomeArray 
} 

अब, दोनों तरीकों एक ही समय में दो अलग-अलग धागे द्वारा पहुँचा रहे हैं। मैं कैसे सुनिश्चित करूं कि myAwesomeArray में कोई समस्या नहीं होगी? क्या मैं NSLock या NSRecursiveLock जैसे कुछ उपयोग करता हूं?

फोल्ड 3:

अब, ऊपर दो मामलों में, myAwesomeArray स्मृति में एक इवर था। अगर मेरे पास डेटाबेस फ़ाइल है, तो मैं हमेशा स्मृति में नहीं रहता हूं। जब भी मैं डेटाबेस ऑपरेशंस करना चाहता हूं, तो मैं databaseManagerInstance बना देता हूं, और इसे पूरा करने के बाद इसे छोड़ देता हूं। इस प्रकार, मूल रूप से, विभिन्न वर्ग डेटाबेस तक पहुंच सकते हैं। प्रत्येक वर्ग DatabaseManger का अपना उदाहरण बनाता है, लेकिन मूल रूप से, वे सभी एक ही, एकल डेटाबेस फ़ाइल का उपयोग कर रहे हैं। मैं कैसे सुनिश्चित करूं कि ऐसी स्थिति में दौड़ की स्थिति के कारण डेटा दूषित नहीं हुआ है?

इससे मुझे अपने कुछ बुनियादी सिद्धांतों को स्पष्ट करने में मदद मिलेगी।

+1

'@ synchronize' एक ही चर है कि आप बंद कर दिया है, नहीं कोड की है कि विशिष्ट ब्लॉक तक पहुँचने से अन्य धागे से बचाता है। –

+0

आह। समझा। खैर, मुझे लगता है कि @synchronize निर्देश के बारे में जो कुछ मैंने समझा था उसके बारे में थोड़ा और कुछ था। धन्यवाद! : डी – codeBearer

+0

@codeBearer मैंने http://stackoverflow.com/a/15393623/412916 किंडा पर आपके प्रश्न का उत्तर दिया। – Jano

उत्तर

41

फोल्ड 1 आम तौर पर @synchronized की आपकी समझ सही है। हालांकि, तकनीकी रूप से, यह कोई कोड "थ्रेड-सुरक्षित" नहीं बनाता है। यह अलग-अलग धागे को एक ही समय में एक ही लॉक से प्राप्त करने से रोकता है, हालांकि आपको यह सुनिश्चित करने की आवश्यकता है कि आप महत्वपूर्ण अनुभागों का प्रदर्शन करते समय हमेशा एक ही सिंक्रनाइज़ेशन टोकन का उपयोग करें। यदि आप ऐसा नहीं करते हैं, तो भी आप उस स्थिति में खुद को ढूंढ सकते हैं जहां दो धागे एक ही समय में महत्वपूर्ण अनुभाग करते हैं। Check the docs

फोल्ड 2 अधिकांश लोग शायद आपको NSRecursiveLock का उपयोग करने की सलाह देंगे। अगर मैं आप थे, तो मैं जीसीडी का उपयोग करूंगा। Here is a great document showing how to migrate from thread programming to GCD programming, मुझे लगता है कि समस्या के लिए यह दृष्टिकोण एनएसएलॉक पर आधारित एक से बेहतर है। संक्षेप में, आप एक धारावाहिक कतार बनाते हैं और अपने कार्यों को उस कतार में प्रेषित करते हैं। इस तरह आप सुनिश्चित करते हैं कि आपके महत्वपूर्ण वर्गों को क्रमशः संभाला जाता है, इसलिए किसी भी समय पर केवल एक महत्वपूर्ण अनुभाग किया जाता है।

फोल्ड 3 यह केवल अधिक विशिष्ट फोल्ड 2 के समान है। डेटा बेस एक संसाधन है, कई मायनों से यह सरणी या किसी अन्य चीज के समान है। If you want to see the GCD based approach in database programming context, take a look at fmdb implementation। यह ठीक है जो मैंने Fold2 में वर्णित किया है।

एक पक्ष नोट के रूप में फोल्ड 3 लिए, मुझे नहीं लगता है कि यह सही दृष्टिकोण है instantiating DatabaseManager हर बार जब आप डेटाबेस का उपयोग करना चाहते हैं और फिर रिहा है। मुझे लगता है कि आपको एक एकल डेटाबेस कनेक्शन बनाना चाहिए और इसे अपने एप्लिकेशन सत्र के माध्यम से बनाए रखना चाहिए। इस तरह इसे प्रबंधित करना आसान है। फिर, एफएमडीबी यह एक महान उदाहरण है कि यह कैसे प्राप्त किया जा सकता है।

संपादित GCD उपयोग करने के लिए तो हाँ, आप तंत्र ताला लगा किसी तरह का उपयोग करने के लिए की आवश्यकता होगी नहीं करना चाहते हैं, और यदि आप अपने तरीकों में प्रत्यावर्तन का उपयोग हाँ, NSRecursiveLock गतिरोध नहीं कर पाएगा, तो यह एक अच्छा विकल्प है (इसका उपयोग @synchronized द्वारा किया जाता है)। हालांकि, एक पकड़ हो सकती है। यदि यह संभव है कि कई धागे एक ही संसाधन के लिए प्रतीक्षा करेंगे और जिस क्रम में उन्हें पहुंच प्राप्त हो, वह प्रासंगिक है, तो NSRecursiveLock पर्याप्त नहीं है। आप अभी भी NSCondition के साथ इस स्थिति का प्रबंधन कर सकते हैं, लेकिन मेरा विश्वास करो, आप इस मामले में जीसीडी का उपयोग करके बहुत समय बचाएंगे। यदि धागे का क्रम प्रासंगिक नहीं है, तो आप ताले से सुरक्षित हैं।

+0

आपके उत्तर के लिए धन्यवाद एक टन! मुझे लगता है कि यह मेरे ज्यादातर संदेहों का ख्याल रखता है। मात्र पुष्टि के रूप में: मेरा अनुमान है कि मैं होगा तह 3 के लिए एक 'NSRecursiveLock' का उपयोग कर के रूप में अच्छी तरह से, मैं थे GCD का उपयोग नहीं करने के लिए, सही है? यह मेरे ज्ञान के आधार के लिए अधिक है, ताकि मेरी अवधारणाएं स्पष्ट हो जाएं। इसके अतिरिक्त, _Fold 3_ पर आपके साइड नोट के लिए धन्यवाद। मैं इस तथ्य से आपसे सहमत हूं कि कक्षा बनाए रखने के लिए सबसे अच्छा है, और आवश्यकता होने पर कनेक्शन को खोलें और बंद करें। मैं बस कुछ कोड दोबारा लिखता हूं जिन्होंने कई तात्कालिकताओं को किया - जो कि एक दुःस्वप्न था। > _ <मैंने इसे एक बेहतर तस्वीर देने के लिए प्रश्न में रखा था। :) – codeBearer

+0

मदद करने में खुशी है, मेरे संपादित उत्तर पर एक नज़र डालें। – lawicko

+0

बिल्कुल सही! धन्यवाद एक टन फिर से! : डी मेरा ज्ञान ++ मैं अपनी सभी नई परियोजनाओं में जीसीडी का उपयोग शुरू करने की योजना बना रहा हूं, और आपके द्वारा प्रदान किए गए लिंक एक महान प्रारंभिक बिंदु हैं! :) – codeBearer

1

सबक्लास एनएसएमयूटेबलएरे, एक्सेसर (पढ़ने और लिखने) विधियों के लिए लॉकिंग प्रदान करने के लिए। कुछ ऐसा:

@interface MySafeMutableArray : NSMutableArray { NSRecursiveLock *lock; } @end 

@implementation MySafeMutableArray 

- (void)addObject:(id)obj { 
    [self.lock lock]; 
    [super addObject: obj]; 
    [self.lock unlock]; 
} 

// ... 
@end 

यह दृष्टिकोण सरणी के हिस्से के रूप में लॉकिंग को समाहित करता है। उपयोगकर्ताओं को अपनी कॉल बदलने की आवश्यकता नहीं है (लेकिन यह पता होना चाहिए कि एक्सेस समय महत्वपूर्ण होने पर वे एक्सेस के लिए ब्लॉक/प्रतीक्षा कर सकते हैं)। इस दृष्टिकोण का एक महत्वपूर्ण लाभ यह है कि यदि आप निर्णय लेते हैं कि आप ताले का उपयोग न करना पसंद करते हैं तो आप प्रेषण कतारों का उपयोग करने के लिए MySafeMutableArray को फिर से कार्यान्वित कर सकते हैं - या जो भी आपकी विशिष्ट समस्या के लिए सबसे अच्छा है। उदाहरण के लिए, आप addObject को लागू कर सकता है के रूप में:

- (void)addObject:(id)obj { 
    dispatch_sync (self.queue, ^{ [super addObject: obj] }); 
} 

नोट: यदि ताले उपयोग कर, आप निश्चित रूप से NSRecursiveLock, नहीं NSLock, की जरूरत है, क्योंकि आप addObject की ऑब्जेक्टिव-सी कार्यान्वयन की नहीं जानता हूँ आदि खुद को कर रहे हैं पुनरावर्ती।

+0

आपके उत्तर के लिए धन्यवाद। यह एक अच्छा विचार है अगर मैं किसी ऑब्जेक्ट का थ्रेड सुरक्षित सबक्लास बनाना चाहता था। :) – codeBearer

+1

यदि आप कॉलर्स तक थ्रेड सुरक्षा छोड़ रहे हैं (यानी, आप थ्रेड सुरक्षित सबक्लास नहीं बना रहे हैं, कभी भी कैसे कार्यान्वित किया जाता है) तो आप करेंगे, दोहराना होगा, समस्याओं के लिए स्वयं को स्थापित कर लेंगे। आप स्पिन प्रौद्योगिकी के बारे में कुछ सीखने के लिए स्वयं की सेवा करेंगे। सौभाग्य। – GoZoner

+0

बहुत बढ़िया। उस टिप के लिए धन्यवाद। मैं इस पर ध्यान दूँगा। : डी – codeBearer

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