NSRunLoop को समझना


108

क्या कोई समझा सकता है कि क्या है NSRunLoop? जैसा कि मुझे पता NSRunLoopहै कि कुछ NSThreadसही के साथ जुड़ा हुआ है? तो मान लीजिए कि मैं एक थ्रेड बनाता हूं

NSThread* th=[[NSThread alloc] initWithTarget:self selector:@selector(someMethod) object:nil];
[th start];

-(void) someMethod
{
    NSLog(@"operation");
}

तो इसके बाद थ्रेड ने अपना काम सही किया? क्यों उपयोग करें RunLoopsया कहां उपयोग करें? Apple डॉक्स से मैंने कुछ पढ़ा है लेकिन यह मेरे लिए स्पष्ट नहीं है, इसलिए कृपया इसे जितना संभव हो उतना सरल समझाएं


यह प्रश्न बहुत व्यापक है। कृपया अपने प्रश्न को और अधिक विशिष्ट करने के लिए परिष्कृत करें।
जॉडी हैगिन्स

3
सबसे पहले मैं यह जानना चाहता हूं कि NSRunLoop में क्या करते हैं और यह थ्रेड के साथ कैसे जुड़ा है
taffarel

जवाबों:


211

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

प्रत्येक NSThread के पास अपना स्वयं का रन लूप होता है, जिसे currentRunLoop विधि के माध्यम से एक्सेस किया जा सकता है।

सामान्य तौर पर, आपको रन लूप को सीधे एक्सेस करने की आवश्यकता नहीं है, हालांकि कुछ (नेटवर्किंग) घटक हैं जो आपको यह निर्दिष्ट करने की अनुमति दे सकते हैं कि वे कौन से रन लूप का उपयोग करेंगे I / O प्रसंस्करण के लिए।

किसी दिए गए धागे के लिए एक रन लूप तब तक इंतजार करेगा जब तक कि उसके एक या अधिक इनपुट स्रोतों में कुछ डेटा या ईवेंट न हो, फिर प्रत्येक इनपुट स्रोत को संसाधित करने के लिए उपयुक्त इनपुट हैंडलर (एस) को आग दें जो "तैयार" है।

ऐसा करने के बाद, यह अपने लूप में वापस आ जाएगा, विभिन्न स्रोतों से प्रोसेसिंग इनपुट, और "सो" अगर कोई काम नहीं करना है।

यह एक उच्च स्तरीय विवरण है (बहुत सारे विवरणों से बचने की कोशिश)।

संपादित करें

टिप्पणी को संबोधित करने का प्रयास। मैंने उसे टुकड़ों में तोड़ दिया।

  • इसका मतलब है कि मैं केवल थ्रेड के अंदर लूप चलाने के लिए उपयोग / रन कर सकता हूं?

वास्तव में। NSRunLoop थ्रेड सुरक्षित नहीं है, और केवल उस थ्रेड के संदर्भ से एक्सेस किया जाना चाहिए जो लूप चला रहा है।

  • क्या कोई सरल उदाहरण है कि लूप को चलाने के लिए घटना कैसे जोड़ें?

यदि आप किसी पोर्ट की निगरानी करना चाहते हैं, तो आप उस पोर्ट को रन लूप में जोड़ देंगे, और फिर रन लूप उस पोर्ट को गतिविधि के लिए देखेगा।

- (void)addPort:(NSPort *)aPort forMode:(NSString *)mode

आप स्पष्ट रूप से एक टाइमर भी जोड़ सकते हैं

- (void)addTimer:(NSTimer *)aTimer forMode:(NSString *)mode
  • इसका क्या अर्थ है कि यह अपने पाश में लौट आएगा?

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

  • जब मैं धागा शुरू करता हूं तो रन लूप निष्क्रिय होता है?

अधिकांश अनुप्रयोगों में, मुख्य रन लूप स्वचालित रूप से चलेगा। हालांकि, आप रन लूप शुरू करने और आपके द्वारा स्पिन किए जाने वाले थ्रेड्स के लिए आने वाली घटनाओं का जवाब देने के लिए जिम्मेदार हैं।

  • क्या थ्रेड के बाहर थ्रेड रन लूप में कुछ ईवेंट जोड़ना संभव है?

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

  • क्या इसका मतलब यह है कि कभी-कभी मैं थ्रेड को ब्लॉक करने के लिए रन लूप का उपयोग कर सकता हूं

वास्तव में। वास्तव में, एक रन लूप एक इवेंट हैंडलर में "ठहर" जाएगा, जब तक कि ईवेंट हैंडलर वापस नहीं आ जाता। आप इसे बस किसी भी ऐप में देख सकते हैं। किसी भी IO कार्रवाई (जैसे, बटन प्रेस) के लिए एक हैंडलर स्थापित करें जो सोता है। आप मुख्य रन लूप (और पूरे UI) को ब्लॉक कर देंगे, जब तक कि वह विधि पूरी नहीं हो जाती।

वही किसी भी रन लूप पर लागू होता है।

मेरा सुझाव है कि आप रन लूप पर निम्नलिखित दस्तावेज पढ़ें:

https://developer.apple.com/documentation/foundation/nsrunloop

और धागे के भीतर उनका उपयोग कैसे किया जाता है:

https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html#//apple_ref/doc/uid/10000057i-CH16-SW1


2
इसका मतलब है कि मैं केवल थ्रेड के अंदर लूप चलाने के लिए एक्सेस / रन कर सकता हूं? क्या कोई सरल उदाहरण है कि लूप को चलाने के लिए घटना कैसे जोड़ें? इसका क्या अर्थ है कि यह अपने पाश में लौट आएगा? जब मैं धागा शुरू करता हूं तो रन लूप निष्क्रिय होता है? क्या थ्रेड के बाहर थ्रेड रन लूप में कुछ ईवेंट जोड़ना संभव है? क्या इसका मतलब है कि कभी-कभी मैं थ्रेड को ब्लॉक करने के लिए रन लूप का उपयोग कर सकता हूं?
तफेल

"किसी भी IO कार्रवाई के लिए एक हैंडलर स्थापित करें (जैसे, बटन प्रेस) जो सोता है।" क्या आपका मतलब है कि अगर मैं बटन पर अपनी उंगली रखना जारी रखता हूं, तो यह थ्रेड को एक समय के लिए ब्लॉक करना जारी रखेगा ?!
हनी

नहीं। मेरा मतलब यह है कि जब तक हैंडलर पूरा नहीं हो जाता है तब तक रनरूप नई घटनाओं को संसाधित नहीं करता है। यदि आप हैंडलर में सोते हैं (या कुछ ऑपरेशन करते हैं जो एक लंबा समय लेता है), तो रन लूप तब तक ब्लॉक रहेगा जब तक हैंडलर ने अपना काम पूरा नहीं कर लिया।
जोड़ी हगिंस

@taffarel क्या थ्रेड के बाहर थ्रेड रन लूप में कुछ घटनाओं को जोड़ना संभव है? अगर इसका मतलब है कि "क्या मैं किसी दूसरे धागे के रनअप पर कोड रन बना सकता हूं" तो जवाब वास्तव में हां है। बस कॉल करें performSelector:onThread:withObject:waitUntilDone:, एक NSThreadऑब्जेक्ट पास करना और आपका चयनकर्ता उस धागे के रनअप पर शेड्यूल किया जाएगा।
मकी

12

रन लूप्स हैं जो इंटरेक्टिव ऐप्स को कमांड-लाइन टूल्स से अलग करते हैं ।

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

से यहाँ

वे आपको उपयोगकर्ता के टैप तक प्रतीक्षा करने और तदनुसार प्रतिक्रिया करने की अनुमति देते हैं, तब तक प्रतीक्षा करें जब तक कि आप एक पूर्णहैंडलर प्राप्त न करें और इसके परिणाम लागू करें, तब तक प्रतीक्षा करें जब तक आप टाइमर प्राप्त न करें और फ़ंक्शन करें। यदि आपके पास कोई रन-अप नहीं है, तो आप उपयोगकर्ता के टैप के लिए सुन / प्रतीक्षा नहीं कर सकते, आप तब तक इंतजार नहीं कर सकते जब तक कि नेटवर्क कॉल नहीं हो रहा हो, आप x मिनटों में जागृत नहीं हो सकते जब तक कि आप उपयोग न करें DispatchSourceTimerयाDispatchWorkItem

इस टिप्पणी से भी :

बैकग्राउंड थ्रेड्स के पास अपना रन-अप नहीं है, लेकिन आप सिर्फ एक जोड़ सकते हैं। उदाहरण के लिए AFNetworking 2.x ने किया। यह कोशिश की गई थी और बैकग्राउंड थ्रेड्स पर NSURLConnection या NSTimer के लिए सच्ची तकनीक थी, लेकिन हम इसे अब और अधिक नहीं करते, क्योंकि नए APIs ऐसा करने की आवश्यकता को समाप्त कर देते हैं। लेकिन ऐसा लगता है कि URLSession करता है, उदाहरण के लिए, यहाँ सरल अनुरोध है , [छवि के बाएं पैनल को देखें] मुख्य कतार पर पूरा हैंडलर, और आप देख सकते हैं कि पृष्ठभूमि थ्रेड पर एक रन लूप है


के बारे में विशेष रूप से: "पृष्ठभूमि धागे के पास अपने स्वयं के रनअप नहीं हैं"। निम्नलिखित टाइमर एक async प्रेषण के लिए आग में विफल रहता है :

class T {
    var timer: Timer?

    func fireWithoutAnyQueue() {
        timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: false, block: { _ in
            print("without any queue") // success. It's being ran on main thread, since playgrounds begin running from main thread
        })
    }

    func fireFromQueueAsnyc() {
        let queue = DispatchQueue(label: "whatever")
        queue.async {
            self.timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: false, block: { (_) in
                print("from a queue — async") // failed to print
            })
        }
    }

    func fireFromQueueSnyc() {
        let queue = DispatchQueue(label: "whatever")
        queue.sync {
            timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: false, block: { (_) in
                print("from a queue — sync") // success. Weird. Read my possible explanation below
            })
        }
    }

    func fireFromMain() {
        DispatchQueue.main.async {
            self.timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: false, block: { (_) in
                print("from main queue — sync") //success
            })
        }
    }
}

मुझे लगता है कि syncब्लॉक का कारण भी यही है:

सिंक ब्लॉक आमतौर पर अपने स्रोत कतार के भीतर से निष्पादित होते हैं । इस उदाहरण में, स्रोत कतार मुख्य कतार है, जो भी कतार गंतव्य कतार है।

यह जांचने के लिए कि मैंने RunLoop.currentहर प्रेषण के अंदर प्रवेश किया है।

सिंक प्रेषण में मुख्य कतार के समान ही रन-अप था । जबकि Async ब्लॉक के भीतर RunLoop दूसरों से अलग उदाहरण था। आप सोच रहे होंगे कि RunLoop.currentएक अलग मूल्य कैसे लौटता है। यह एक साझा मूल्य नहीं है !? बड़ा अच्छा सवाल! आगे पढ़िए:

महत्वपूर्ण लेख:

वर्ग संपत्ति current एक वैश्विक चर नहीं है।

वर्तमान थ्रेड के लिए रन लूप लौटाता है ।

यह प्रासंगिक है। यह केवल थ्रेड यानी थ्रेड-लोकल स्टोरेज के दायरे में दिखाई देता है । उस पर अधिक के लिए यहां देखें ।

यह टाइमर के साथ एक ज्ञात मुद्दा है। यदि आप उपयोग करते हैं तो आपके पास एक ही मुद्दा नहीं हैDispatchSourceTimer


8

RunLoops एक बॉक्स की तरह होते हैं, जहाँ सामान बस होता है।

मूल रूप से, एक RunLoop में, आप कुछ ईवेंट्स को संसाधित करने और फिर वापस जाने के लिए जाते हैं। या टाइमआउट हिट होने से पहले यह किसी भी घटना को संसाधित नहीं करता है तो वापस लौटें। आप इसे अतुल्यकालिक NSURLConnections के समान कह सकते हैं, अपने वर्तमान लूप को बाधित किए बिना पृष्ठभूमि में डेटा को संसाधित कर सकते हैं और साथ ही साथ, आपको समकालिक रूप से डेटा की आवश्यकता होती है। जिसे RunLoop की मदद से किया जा सकता है जो आपके एसिंक्रोनस को बनाता है NSURLConnectionऔर कॉलिंग टाइम पर डेटा प्रदान करता है। आप इस तरह एक RunLoop का उपयोग कर सकते हैं:

NSDate *loopUntil = [NSDate dateWithTimeIntervalSinceNow:0.1];

while (YourBoolFlag && [[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode beforeDate:loopUntil]) {
    loopUntil = [NSDate dateWithTimeIntervalSinceNow:0.1];
}

इस RunLoop में, यह तब तक चलेगा जब तक आप अपना कुछ और काम पूरा नहीं कर लेते हैं और YourBoolFlag को झूठा कर देते हैं

इसी तरह, आप उन्हें थ्रेड्स में उपयोग कर सकते हैं।

आशा है कि यह आपकी मदद करता है।


0

रन लूप थ्रेड्स के साथ जुड़े बुनियादी ढांचे का हिस्सा हैं। रन लूप एक इवेंट प्रोसेसिंग लूप है जिसका उपयोग आप काम को शेड्यूल करने के लिए करते हैं और आने वाली घटनाओं की प्राप्ति को समन्वित करते हैं। एक रन लूप का उद्देश्य आपके धागे को व्यस्त रखना है जब कोई काम करना है और जब कोई नहीं है तो अपने धागे को सोने के लिए रख दें।

यहां से


CFRunLoop की सबसे महत्वपूर्ण विशेषता CFRunLoopModes है। CFRunLoop "रन लूप स्रोत" की एक प्रणाली के साथ काम करता है। स्रोतों को एक या कई मोड के लिए रन लूप पर पंजीकृत किया जाता है, और रन लूप को दिए गए मोड में चलाने के लिए बनाया जाता है। जब कोई घटना किसी स्रोत पर आती है, तो इसे केवल रन लूप द्वारा नियंत्रित किया जाता है यदि स्रोत मोड रन लूप वर्तमान मोड से मेल खाता है।

यहां से

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.