Computer >> कंप्यूटर ट्यूटोरियल >  >> स्मार्टफोन्स >> iPhone

iOS कंकरेंसी में मास्टरी: GCD क्यू और एक्ज़ीक्यूशन मोड को समझना

iOS में कंकरेंसी एक विशाल विषय है। इसलिए इस लेख में मैं क्यू और ग्रैंड सेंट्रल डिस्पैच (GCD) फ्रेमवर्क से संबंधित एक उप-विषय पर ज़ूम इन करना चाहता हूं।

विशेष रूप से, मैं सीरियल और कंकरेंट क्यू के बीच के अंतरों का पता लगाना चाहता हूं, साथ ही सिंक्रोनस और एसिंक्रोनस एक्ज़ीक्यूशन के बीच के अंतरों का।

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

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

मैं GCD के परिचय से शुरू करूंगा, उसके बाद सिंक, एसिंक, सीरियल और कंकरेंट पर विस्तृत व्याख्या। बाद में, मैं कंकरेंसी के साथ काम करते समय कुछ नुकसानों को कवर करूंगा। अंत में, मैं सारांश और कुछ सामान्य सलाह के साथ समाप्त करूंगा।

परिचय

आइए GCD और डिस्पैच क्यू के संक्षिप्त परिचय से शुरू करें। यदि आप पहले से ही इस विषय से परिचित हैं तो सिंक बनाम एसिंक सेक्शन पर जाने के लिए स्वतंत्र महसूस करें।

कंकरेंसी और ग्रैंड सेंट्रल डिस्पैच

कंकरेंसी आपको इस तथ्य का लाभ उठाने देती है कि आपके डिवाइस में मल्टीपल CPU कोर हैं। इन कोर का उपयोग करने के लिए, आपको मल्टीपल थ्रेड्स का उपयोग करने की आवश्यकता होगी। हालांकि, थ्रेड्स एक लो-लेवल टूल हैं, और थ्रेड्स को मैन्युअली कुशल तरीके से प्रबंधित करना बेहद मुश्किल है।

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

GCD के साथ, Apple ने समस्या के लिए एक एसिंक्रोनस डिज़ाइन दृष्टिकोण लिया। सीधे थ्रेड्स बनाने के बजाय, आप काम के टास्क को शेड्यूल करने के लिए GCD का उपयोग करते हैं, और सिस्टम अपने संसाधनों का सर्वोत्तम उपयोग करके आपके लिए इन टास्क को करेगा। GCD आपके लिए आवश्यक थ्रेड्स बनाने और उन थ्रेड्स पर आपके टास्क को शेड्यूल करने को संभालेगा, थ्रेड प्रबंधन के बोझ को डेवलपर से सिस्टम पर स्थानांतरित करेगा।

GCD का एक बड़ा फायदा यह है कि जब आप अपना कंकरेंट कोड लिखते हैं तो आपको हार्डवेयर संसाधनों के बारे में चिंता करने की ज़रूरत नहीं है। GCD आपके लिए एक थ्रेड पूल प्रबंधित करता है, और यह सिंगल-कोर Apple Watch से लेकर कई-कोर MacBook Pro तक स्केल करेगा।

डिस्पैच क्यू

ये GCD के मुख्य बिल्डिंग ब्लॉक्स हैं जो आपको आपके द्वारा परिभाषित पैरामीटर्स के एक सेट का उपयोग करके कोड के मनमाने ब्लॉक्स को निष्पादित करने देते हैं। डिस्पैच क्यू में टास्क हमेशा फर्स्ट-इन, फर्स्ट-आउट (FIFO) तरीके से शुरू होते हैं। ध्यान दें कि मैंने शुरू कहा, क्योंकि आपके टास्क का पूरा होने का समय कई कारकों पर निर्भर करता है, और FIFO होने की गारंटी नहीं है (इस पर बाद में और अधिक।)

मोटे तौर पर, आपके लिए तीन प्रकार की क्यू उपलब्ध हैं:

  • मेन डिस्पैच क्यू (सीरियल, पूर्व-परिभाषित)
  • ग्लोबल क्यू (कंकरेंट, पूर्व-परिभाषित)
  • प्राइवेट क्यू (सीरियल या कंकरेंट हो सकते हैं, आप उन्हें बनाते हैं)

हर ऐप एक मेन क्यू के साथ आता है, जो एक सीरियल क्यू है जो मेन थ्रेड पर टास्क निष्पादित करता है। यह क्यू आपके एप्लिकेशन के UI को ड्रॉ करने और उपयोगकर्ता इंटरैक्शन (टच, स्क्रॉल, पैन, आदि) का जवाब देने के लिए जिम्मेदार है। यदि आप इस क्यू को बहुत लंबे समय तक ब्लॉक करते हैं, तो आपका iOS ऐप फ्रीज हुआ प्रतीत होगा, और आपका macOS ऐप कुख्यात बीच बॉल/स्पिनिंग व्हील प्रदर्शित करेगा।

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

URLSession.shared.dataTask(with: url) { data, response, error in
    if let data = data {
        DispatchQueue.main.async {
            // UI वर्क
            self.label.text = String(data: data, encoding: .utf8)
        }
    }
}
URLSession बैकग्राउंड क्यू पर कॉलबैक देता है

एक नियम के रूप में, सभी UI वर्क को मेन क्यू पर निष्पादित किया जाना चाहिए। आप Xcode में मेन थ्रेड चेकर विकल्प चालू कर सकते हैं जब भी UI वर्क बैकग्राउंड थ्रेड पर निष्पादित होता है तो चेतावनी प्राप्त करने के लिए।

iOS कंकरेंसी में मास्टरी: GCD क्यू और एक्ज़ीक्यूशन मोड को समझना

मेन क्यू के अलावा, हर ऐप कई पूर्व-परिभाषित कंकरेंट क्यू के साथ आता है जिनमें Quality of Service (GCD में प्राथमिकता की एक अमूर्त धारणा) के विभिन्न स्तर होते हैं।

उदाहरण के लिए, यहां यूजर इंटरैक्टिव (उच्चतम प्राथमिकता) QoS क्यू पर एसिंक्रोनसली काम सबमिट करने का कोड है:

DispatchQueue.global(qos: .userInteractive).async {
    print("We're on a global concurrent queue!")
}

वैकल्पिक रूप से, आप QoS निर्दिष्ट न करके डिफ़ॉल्ट प्राथमिकता ग्लोबल क्यू को इस तरह कॉल कर सकते हैं:

DispatchQueue.global().async {
    print("Generic global queue")
}
डिफ़ॉल्ट QoS यूजर इनिशिएटेड और यूटिलिटी के बीच कहीं पड़ता है

इसके अलावा, आप निम्न सिंटैक्स का उपयोग करके अपनी खुद की प्राइवेट क्यू बना सकते हैं:

let serial = DispatchQueue(label: "com.besher.serial-queue")
serial.async {
    print("Private serial queue")
}
प्राइवेट क्यू डिफ़ॉल्ट रूप से सीरियल होती हैं

प्राइवेट क्यू बनाते समय, एक वर्णनात्मक लेबल (जैसे रिवर्स DNS नोटेशन) का उपयोग करना मददगार होता है, क्योंकि यह Xcode के नेविगेटर, lldb, और Instruments में डीबगिंग करते समय आपकी सहायता करेगा:

iOS कंकरेंसी में मास्टरी: GCD क्यू और एक्ज़ीक्यूशन मोड को समझना
iOS कंकरेंसी में मास्टरी: GCD क्यू और एक्ज़ीक्यूशन मोड को समझना

डिफ़ॉल्ट रूप से, प्राइवेट क्यू सीरियल होती हैं (मैं जल्द ही बताऊंगा कि इसका क्या अर्थ है, वादा!) यदि आप एक प्राइवेट कंकरेंट क्यू बनाना चाहते हैं, तो आप वैकल्पिक एट्रिब्यूट्स पैरामीटर के माध्यम से ऐसा कर सकते हैं:

let concurrent = DispatchQueue(label: "com.besher.serial-queue", attributes: .concurrent)
concurrent.sync {
    print("Private concurrent queue")
}

एक वैकल्पिक QoS पैरामीटर भी है। आपके द्वारा बनाई गई प्राइवेट क्यू अंततः उनके दिए गए पैरामीटर्स के आधार पर ग्लोबल कंकरेंट क्यू में से एक में लैंड करेगी।

टास्क में क्या है?

मैंने क्यू में टास्क डिस्पैच करने का उल्लेख किया। टास्क कोड के किसी भी ब्लॉक को संदर्भित कर सकते हैं जिसे आप sync या async फ़ंक्शन का उपयोग करके क्यू में सबमिट करते हैं। उन्हें एक अनाम क्लोजर के रूप में सबमिट किया जा सकता है:

DispatchQueue.global().async {
    print("Anonymous closure")
}

या एक डिस्पैच वर्क आइटम के अंदर जो बाद में निष्पादित होता है:

let item = DispatchWorkItem(qos: .utility) {
    print("Work item to be executed later")
}
ध्यान दें कि हमने यहां टास्क QoS को कैसे परिभाषित किया

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

उदाहरण के लिए, यदि आपके पास एक ही टास्क के अंदर 3 लूप हैं, तो ये लूप हमेशा क्रम में निष्पादित होंगे:

DispatchQueue.global().async {
    for i in 0..<10 { print(i) }
    for _ in 0..<10 { print("🔵") }
    for _ in 0..<10 { print("💔") }
}

यह कोड हमेशा 0 से 9 तक दस अंक प्रिंट करता है, उसके बाद दस नीले गोले, उसके बाद दस टूटे दिल, चाहे आप उस क्लोजर को कैसे भी डिस्पैच करें।

व्यक्तिगत टास्क का अपना QoS लेवल भी हो सकता है (डिफ़ॉल्ट रूप से वे अपनी क्यू की प्राथमिकता का उपयोग करते हैं।) क्यू QoS और टास्क QoS के बीच यह अंतर कुछ दिलचस्प व्यवहार की ओर ले जाता है जिस पर हम प्राथमिकता उलटफेर सेक्शन में चर्चा करेंगे।

अब तक आप सोच रहे होंगे कि सीरियल और कंकरेंट क्या हैं। आप sync और async के बीच के अंतरों के बारे में भी सोच रहे होंगे जब आप अपने टास्क सबमिट करते हैं। यह इस लेख का सार है, तो चलिए गोता लगाते हैं!

सिंक बनाम एसिंक

जब आप एक टास्क को क्यू में डिस्पैच करते हैं, तो आप इसे सिंक्रोनसली या एसिंक्रोनसली sync और async डिस्पैच फ़ंक्शन का उपयोग करके करना चुन सकते हैं। सिंक और एसिंक मुख्य रूप से सबमिट किए गए टास्क के स्रोत को प्रभावित करते हैं, यानी वह क्यू जहां से इसे सबमिट किया जा रहा है से।

जब आपका कोड sync स्टेटमेंट तक पहुंचता है, तो यह वर्तमान क्यू को तब तक ब्लॉक करेगा जब तक वह टास्क पूरा नहीं हो जाता। एक बार टास्क रिटर्न/पूरा हो जाने पर, कंट्रोल कॉलर को वापस कर दिया जाता है, और sync टास्क के बाद आने वाला कोड जारी रहता है।

sync को 'ब्लॉकिंग' का पर्यायवाची समझें।

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

वर्तमान क्यू?

यह स्पष्ट नहीं हो सकता कि स्रोत, या वर्तमान, क्यू क्या है, क्योंकि यह कोड में हमेशा स्पष्ट रूप से परिभाषित नहीं होता है।

उदाहरण के लिए, यदि आप अपने sync स्टेटमेंट को viewDidLoad के अंदर कॉल करते हैं, तो आपकी वर्तमान क्यू मेन डिस्पैच क्यू होगी। यदि आप उसी फ़ंक्शन को URLSession कम्पलीशन हैंडलर के अंदर कॉल करते हैं, तो आपकी वर्तमान क्यू एक बैकग्राउंड क्यू होगी।

सिंक बनाम एसिंक पर वापस जाते हुए, आइए इस उदाहरण को लें:

DispatchQueue.global().sync {
    print("Inside")
}
print("Outside")
// कंसोल आउटपुट:
// Inside
// Outside

उपरोक्त कोड वर्तमान क्यू को ब्लॉक करेगा, क्लोजर में प्रवेश करेगा और ग्लोबल क्यू पर "Inside" प्रिंट करके इसके कोड को निष्पादित करेगा, फिर "Outside" प्रिंट करने के लिए आगे बढ़ेगा। यह क्रम गारंटीकृत है।

आइए देखते हैं कि अगर हम इसके बजाय async आज़माते हैं तो क्या होता है:

DispatchQueue.global().async {
    print("Inside")
}
print("Outside")
// संभावित कंसोल आउटपुट (QoS के आधार पर):
// Outside
// Inside

हमारा कोड अब क्लोजर को ग्लोबल क्यू पर सबमिट करता है, फिर तुरंत अगली लाइन चलाने के लिए आगे बढ़ता है। यह संभावना है कि "Inside" से पहले "Outside" प्रिंट करेगा, लेकिन यह क्रम गारंटीकृत नहीं है। यह स्रोत और गंतव्य क्यू के QoS के साथ-साथ अन्य कारकों पर निर्भर करता है जिन्हें सिस्टम नियंत्रित करता है।

थ्रेड्स GCD में एक कार्यान्वयन विवरण हैं — हमारा उन पर सीधा नियंत्रण नहीं है और हम केवल क्यू एब्स्ट्रैक्शन का उपयोग करके उनसे निपट सकते हैं। फिर भी, मुझे लगता है कि GCD के साथ हमारे सामने आने वाली कुछ चुनौतियों को समझने के लिए थ्रेड व्यवहार को 'अंदर झांकना' उपयोगी हो सकता है।

उदाहरण के लिए, जब आप sync का उपयोग करके एक टास्क सबमिट करते हैं, तो GCD कॉलर (कॉलर) पर उस टास्क को निष्पादित करके प्रदर्शन को अनुकूलित करता है।

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

iOS कंकरेंसी में मास्टरी: GCD क्यू और एक्ज़ीक्यूशन मोड को समझना
GitHub पर Dispatcher से

कौन सा उपयोग करें?

क्यू में काम सबमिट करते समय, Apple एसिंक्रोनस एक्ज़ीक्यूशन को सिंक्रोनस एक्ज़ीक्यूशन पर उपयोग करने की सिफारिश करता है। हालांकि, ऐसी स्थितियां हैं जहां sync बेहतर विकल्प हो सकता है, जैसे रेस कंडीशन से निपटने में, या बहुत छोटा टास्क करते समय। मैं इन स्थितियों को शीघ्र ही कवर करूंगा।

एक फ़ंक्शन के अंदर एसिंक्रोनसली काम करने का एक बड़ा परिणाम यह है कि फ़ंक्शन अब सीधे अपने मान वापस नहीं कर सकता (यदि वे किए जा रहे एसिंक वर्क पर निर्भर करते हैं)। इसके बजाय इसे परिणाम देने के लिए एक क्लोजर/कम्पलीशन हैंडलर पैरामीटर का उपयोग करना होगा।

इस अवधारणा को प्रदर्शित करने के लिए, आइए एक छोटा फ़ंक्शन लें जो इमेज डेटा स्वीकार करता है, इमेज को प्रोसेस करने के लिए कुछ महंगी कम्प्यूटेशन करता है, फिर परिणाम लौटाता है:

func processImage(data: Data) -> UIImage? {
    guard let image = UIImage(data: data) else { return nil }
    // एक महंगी फ़ंक्शन कॉल करना
    let processedImage = upscaleAndFilter(image: image)
    return processedImage
}

इस उदाहरण में, upscaleAndFilter(image:) फ़ंक्शन में कई सेकंड लग सकते हैं, इसलिए हम इसे UI को फ्रीज होने से बचाने के लिए एक अलग क्यू में ऑफलोड करना चाहते हैं। आइए इमेज प्रोसेसिंग के लिए एक समर्पित क्यू बनाएं, और फिर महंगी फ़ंक्शन को एसिंक्रोनसली डिस्पैच करें:

let imageProcessingQueue = DispatchQueue(label: "com.besher.image-processing")
func processImageAsync(data: Data) -> UIImage? {
    guard let image = UIImage(data: data) else { return nil }
    imageProcessingQueue.async {
        let processedImage = upscaleAndFilter(image: image)
        return processedImage
    }
}
यह कोड कंपाइल नहीं होता!

इस कोड में दो समस्याएं हैं। पहला, रिटर्न स्टेटमेंट एसिंक क्लोजर के अंदर है, इसलिए यह अब processImageAsync(data:) फ़ंक्शन को मान वापस नहीं कर रहा है, और वर्तमान में कोई उद्देश्य नहीं रखता है।

लेकिन बड़ी समस्या यह है कि हमारा processImageAsync(data:) फ़ंक्शन अब कोई मान वापस नहीं कर रहा है, क्योंकि फ़ंक्शन अपने बॉडी के अंत तक पहुंचने से पहले async क्लोजर में प्रवेश करता है।

इस त्रुटि को ठीक करने के लिए, हम फ़ंक्शन को एडजस्ट करेंगे ताकि यह सीधे मान वापस न करे। इसके बजाय, इसमें एक नया कम्पलीशन हैंडलर पैरामीटर होगा जिसे हम एक बार हमारे एसिंक्रोनस फ़ंक्शन द्वारा अपना काम पूरा करने पर कॉल कर सकते हैं:

let imageProcessingQueue = DispatchQueue(label: "com.besher.image-processing")
func processImageAsync(data: Data, completion: @escaping (UIImage?) -> Void) {
    guard let image = UIImage(data: data) else {
        completion(nil)
        return
    }
    imageProcessingQueue.async {
        let processedImage = self.upscaleAndFilter(image: image)
        completion(processedImage)
    }
}

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

इसके विपरीत, सिंक्रोनस एक्ज़ीक्यूशन जटिलता नहीं बढ़ाता। बल्कि, यह आपको पहले की तरह रिटर्न स्टेटमेंट का उपयोग जारी रखने की अनुमति देता है। एक sync टास्क वाला फ़ंक्शन तब तक वापस नहीं आएगा जब तक उस टास्क के अंदर का कोड पूरा नहीं हो जाता। इसलिए इसे कम्पलीशन हैंडलर की आवश्यकता नहीं है।

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

हालांकि, यदि आप एक बड़ा टास्क सबमिट कर रहे हैं, जैसे हमने ऊपर इमेज प्रोसेसिंग की, तो कॉलर को बहुत लंबे समय तक ब्लॉक करने से बचने के लिए इसे एसिंक्रोनसली करने पर विचार करें।

उसी क्यू पर डिस्पैच करना

जहां एक क्यू से उसी क्यू में एसिंक्रोनसली टास्क डिस्पैच करना सुरक्षित है (उदाहरण के लिए, आप वर्तमान क्यू पर .asyncAfter का उपयोग कर सकते हैं), आप एक क्यू से उसी क्यू में सिंक्रोनसली टास्क डिस्पैच नहीं कर सकते। ऐसा करने से डेडलॉक होगा जो ऐप को तुरंत क्रैश कर देगा!

यह समस्या तब प्रकट हो सकती है जब सिंक्रोनस कॉल की एक श्रृंखला करना जो मूल क्यू पर वापस ले जाती है। यानी, आप किसी अन्य क्यू पर एक टास्क sync करते हैं, और जब टास्क पूरा हो जाता है, तो यह परिणामों को मूल क्यू में वापस sync करता है, जिससे डेडलॉक होता है। ऐसे क्रैश से बचने के लिए async का उपयोग करें।

मेन क्यू को ब्लॉक करना

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

iOS कंकरेंसी में मास्टरी: GCD क्यू और एक्ज़ीक्यूशन मोड को समझना
मेन क्यू से एसिंक का उपयोग करना पसंद करें

सीरियल बनाम कंकरेंट

सीरियल और कंकरेंट गंतव्य को प्रभावित करते हैं — वह क्यू जिसमें आपका काम चलाने के लिए सबमिट किया गया है। यह सिंक और एसिंक के विपरीत है, जो स्रोत को प्रभावित करते थे।

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

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

iOS कंकरेंसी में मास्टरी: GCD क्यू और एक्ज़ीक्यूशन मोड को समझना
GitHub पर Dispatcher से

एक कंकरेंट क्यू मल्टीपल थ्रेड्स स्पॉन कर सकती है, और सिस्टम तय करता है कि कितने थ्रेड बनाए जाएं। टास्क हमेशा FIFO क्रम में शुरू होते हैं, लेकिन क्यू अगले टास्क को शुरू करने से पहले टास्क के खत्म होने की प्रतीक्षा नहीं करती, इसलिए कंकरेंट क्यू पर टास्क किसी भी क्रम में खत्म हो सकते हैं।

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

iOS कंकरेंसी में मास्टरी: GCD क्यू और एक्ज़ीक्यूशन मोड को समझना
GitHub पर Dispatcher से

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

यह ध्यान रखना महत्वपूर्ण है कि सीरियल बनाम कंकरेंट की अवधारणा केवल एक विशिष्ट क्यू पर चर्चा करते समय प्रासंगिक है। सभी क्यू एक-दूसरे के सापेक्ष कंकरेंट हैं।

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

मल्टीपल सीरियल क्यू की कंकरेंसी को प्रदर्शित करने के लिए, आइए इस उदाहरण को लें:

let serial1 = DispatchQueue(label: "com.besher.serial1")
let serial2 = DispatchQueue(label: "com.besher.serial2")
serial1.async {
    for _ in 0..<5 { print("🔴") }
}
serial2.async {
    for _ in 0..<5 { print("🔵") }
}
iOS कंकरेंसी में मास्टरी: GCD क्यू और एक्ज़ीक्यूशन मोड को समझना

यहां दोनों क्यू सीरियल हैं, लेकिन परिणाम जंबल्ड हैं क्योंकि वे एक-दूसरे के संबंध में कंकरेंटली निष्पादित होते हैं। तथ्य यह है कि वे प्रत्येक सीरियल (या कंकरेंट) हैं, इस परिणाम पर कोई प्रभाव नहीं डालता। उनका QoS लेवल निर्धारित करता है कि कौन आम तौर पर पहले खत्म करेगा (क्रम गारंटीकृत नहीं)।

यदि हम यह सुनिश्चित करना चाहते हैं कि पहला लूप दूसरे लूप को शुरू करने से पहले खत्म हो जाए, तो हम पहले टास्क को कॉलर से सिंक्रोनसली सबमिट कर सकते हैं:

let serial1 = DispatchQueue(label: "com.besher.serial1")
let serial2 = DispatchQueue(label: "com.besher.serial2")
serial1.sync { // <---- हमने इसे 'sync' में बदल दिया
    for _ in 0..<5 { print("🔴") }
}
// हम यहां तक नहीं पहुंचते जब तक पहला लूप समाप्त नहीं हो जाता
serial2.async {
    for _ in 0..<5 { print("🔵") }
}
iOS कंकरेंसी में मास्टरी: GCD क्यू और एक्ज़ीक्यूशन मोड को समझना

यह जरूरी नहीं कि वांछनीय हो, क्योंकि अब हम कॉलर को ब्लॉक कर रहे हैं जबकि पहला लूप निष्पादित हो रहा है।

कॉलर को ब्लॉक किए बिना, हम दोनों टास्क को एसिंक्रोनसली सबमिट कर सकते हैं, लेकिन उसी सीरियल क्यू पर:

let serial = DispatchQueue(label: "com.besher.serial")
serial.async {
    for _ in 0..<5 { print("🔴") }
}
serial.async {
    for _ in 0..<5 { print("🔵") }
}
iOS कंकरेंसी में मास्टरी: GCD क्यू और एक्ज़ीक्यूशन मोड को समझना

अब हमारे टास्क कॉलर के संबंध में कंकरेंटली निष्पादित होते हैं, जबकि उनका क्रम भी बरकरार रहता है।

ध्यान दें कि यदि हम वैकल्पिक पैरामीटर के माध्यम से अपनी सिंगल क्यू को कंकरेंट बनाते हैं, तो हम जंबल्ड परिणामों पर वापस चले जाते हैं, जैसा कि अपेक्षित था:

let concurrent = DispatchQueue(label: "com.besher.concurrent", attributes: .concurrent)
concurrent.async {
    for _ in 0..<5 { print("🔴") }
}
concurrent.async {
    for _ in 0..<5 { print("🔵") }
}
iOS कंकरेंसी में मास्टरी: GCD क्यू और एक्ज़ीक्यूशन मोड को समझना

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

let concurrent = DispatchQueue(label: "com.besher.concurrent", attributes: .concurrent)
concurrent.sync {
    for _ in 0..<5 { print("🔴") }
}
concurrent.async {
    for _ in 0..<5 { print("🔵") }
}
यह भ्रामक हो सकता है
iOS कंकरेंसी में मास्टरी: GCD क्यू और एक्ज़ीक्यूशन मोड को समझना

अचानक, हमारे परिणाम वापस सही क्रम में हैं। लेकिन यह एक कंकरेंट क्यू है, तो ऐसा कैसे हो सकता है? क्या sync स्टेटमेंट ने इसे किसी तरह सीरियल क्यू में बदल दिया?

उत्तर है नहीं!

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

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

कौन सा उपयोग करें?

सीरियल क्यू CPU ऑप्टिमाइजेशन और कैशिंग का लाभ उठाती हैं, और कॉन्टेक्स्ट स्विचिंग को कम करने में मदद करती हैं।

Apple आपके ऐप में प्रति सबसिस्टम एक सीरियल क्यू से शुरू करने की सिफारिश करता है — उदाहरण के लिए नेटवर्किंग के लिए एक, फाइल कम्प्रेशन के लिए एक, आदि। यदि आवश्यकता उत्पन्न होती है, तो आप बाद में setTarget मेथड या क्यू बनाते समय वैकल्पिक टारगेट पैरामीटर का उपयोग करके प्रति सबसिस्टम क्यू के पदानुक्रम का विस्तार कर सकते हैं।

यदि आप एक प्रदर्शन बॉटलनेक में चलते हैं, तो अपने ऐप के प्रदर्शन को मापें फिर देखें कि क्या एक कंकरेंट क्यू मदद करता है। यदि आपको मापने योग्य लाभ नहीं दिखता है, तो सीरियल क्यू पर टिके रहना बेहतर है।

नुकसान

प्राथमिकता उलटफेर और क्वालिटी ऑफ सर्विस

प्राथमिकता उलटफेर तब होता है जब एक हाई प्रायोरिटी टास्क को लोअर प्रायोरिटी टास्क द्वारा चलने से रोका जाता है, जिससे उनकी सापेक्ष प्राथमिकताएं प्रभावी रूप से उलट जाती हैं।

यह स्थिति अक्सर तब होती है जब एक हाई QoS क्यू एक लो QoS क्यू के साथ एक संसाधन साझा करती है, और लो QoS क्यू उस संसाधन पर लॉक प्राप्त कर लेती है।

लेकिन मैं एक अलग परिदृश्य को कवर करना चाहता हूं जो हमारी चर्चा के लिए अधिक प्रासंगिक है — यह तब होता है जब आप एक लो QoS सीरियल क्यू पर टास्क सबमिट करते हैं, फिर उसी क्यू पर एक हाई QoS टास्क सबमिट करते हैं। यह परिदृश्य भी प्राथमिकता उलटफेर का परिणाम देता है, क्योंकि हाई QoS टास्क को लोअर QoS टास्क के खत्म होने का इंतजार करना पड़ता है।

GCD उस क्यू के QoS को अस्थायी रूप से बढ़ाकर प्राथमिकता उलटफेर को हल करता है जिसमें आपके हाई प्रायोरिटी टास्क से 'आगे' या ब्लॉकिंग करने वाले लो प्रायोरिटी टास्क होते हैं।

यह एक एम्बुलेंस के सामने फंसी कारों की तरह है। अचानक उन्हें रेड लाइट पार करने की अनुमति दी जाती है ताकि एम्बुलेंस चल सके (वास्तव में कारें साइड में चली जाती हैं, लेकिन एक संकीर्ण (सीरियल) सड़क या कुछ कल्पना करें, आपको बात समझ आ गई :-P)

उलटफेर समस्या को स्पष्ट करने के लिए, आइए इस कोड से शुरू करें:

enum Color: String {
    case blue = "🔵"
    case white = "⚪️"
}
func output(color: Color, times: Int) {
    for _ in 1...times {
        print(color.rawValue)
    }
}
let starterQueue = DispatchQueue(label: "com.besher.starter", qos: .userInteractive)
let utilityQueue = DispatchQueue(label: "com.besher.utility", qos: .utility)
let backgroundQueue = DispatchQueue(label: "com.besher.background", qos: .background)
let count = 10
starterQueue.async {
    backgroundQueue.async {
        output(color: .white, times: count)
    }
    backgroundQueue.async {
        output(color: .white, times: count)
    }
    utilityQueue.async {
        output(color: .blue, times: count)
    }
    utilityQueue.async {
        output(color: .blue, times: count)
    }
    // अगला स्टेटमेंट यहां जाता है
}

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

चूंकि ये टास्क एसिंक्रोनसली सबमिट किए गए हैं, हर बार जब आप ऐप चलाते हैं, तो आप थोड़े अलग परिणाम देखने जा रहे हैं। हालांकि, जैसा कि आप उम्मीद करेंगे, लोअर QoS (बैकग्राउंड) वाली क्यू लगभग हमेशा अंत में खत्म होती है। वास्तव में, अंतिम 10-15 सर्कल आमतौर पर सभी सफेद होते हैं।

iOS कंकरेंसी में मास्टरी: GCD क्यू और एक्ज़ीक्यूशन मोड को समझना
कोई आश्चर्य नहीं

लेकिन देखें क्या होता है जब हम अंतिम एसिंक स्टेटमेंट के बाद बैकग्राउंड क्यू पर एक सिंक टास्क सबमिट करते हैं। आपको sync स्टेटमेंट के अंदर कुछ भी प्रिंट करने की आवश्यकता नहीं है, बस यह लाइन जोड़ना पर्याप्त है:

// इसे अंतिम एसिंक स्टेटमेंट के बाद जोड़ें,
// अभी भी starterQueue.async के अंदर
backgroundQueue.sync {}
iOS कंकरेंसी में मास्टरी: GCD क्यू और एक्ज़ीक्यूशन मोड को समझना
प्राथमिकता उलटफेर

कंसोल में परिणाम उलट गए हैं! अब, हाई प्रायोरिटी क्यू (यूटिलिटी) हमेशा अंत में खत्म होती है, और अंतिम 10-15 सर्कल नीले हैं।

यह समझने के लिए कि ऐसा क्यों होता है, हमें इस तथ्य को फिर से देखने की आवश्यकता है कि सिंक्रोनस वर्क कॉलर थ्रेड पर निष्पादित होता है (जब तक आप मेन क्यू पर सबमिट नहीं कर रहे हैं।)

ऊपर हमारे उदाहरण में, कॉलर (starterQueue) में टॉप QoS (userInteractive) है। इसलिए, वह प्रतीत होता है निर्दोष sync टास्क न केवल स्टार्टर क्यू को ब्लॉक कर रहा है, बल्कि यह स्टार्टर के हाई QoS थ्रेड पर भी चल रहा है। टास्क इसलिए हाई QoS के साथ चलता है, लेकिन उसी बैकग्राउंड क्यू पर इसके आगे दो अन्य टास्क हैं जिनमें बैकग्राउंड QoS है। प्राथमिकता उलटफेर का पता चला!

अपेक्षा के अनुरूप, GCD पूरे क्यू के QoS को अस्थायी रूप से बढ़ाकर इस उलटफेर को हल करता है ताकि हाई QoS टास्क से मेल खा सके। नतीजतन, बैकग्राउंड क्यू पर सभी टास्क यूजर इंटरैक्टिव QoS पर चलते हैं, जो यूटिलिटी QoS से अधिक है। और इसलिए यूटिलिटी टास्क अंत में खत्म होती हैं!

साइड-नोट: यदि आप उस उदाहरण से स्टार्टर क्यू को हटाते हैं और इसके बजाय मेन क्यू से सबमिट करते हैं, तो आपको समान परिणाम मिलेंगे, क्योंकि मेन क्यू में भी यूजर इंटरैक्टिव QoS है।

इस उदाहरण में प्राथमिकता उलटफेर से बचने के लिए, हमें sync स्टेटमेंट के साथ स्टार्टर क्यू को ब्लॉक करने से बचने की आवश्यकता है। async का उपयोग करने से वह समस्या हल हो जाएगी।

हालांकि यह हमेशा आदर्श नहीं है, आप डिफ़ॉल्ट QoS पर टिके रहकर प्राइवेट क्यू बनाते समय या ग्लोबल कंकरेंट क्यू पर डिस्पैच करते समय प्राथमिकता उलटफेर को कम कर सकते हैं।

थ्रेड एक्सप्लोजन

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

यही कारण है कि Apple आपके ऐप में प्रति सबसिस्टम एक सीरियल क्यू से शुरू करने का सुझाव देता है, क्योंकि प्रत्येक सीरियल क्यू केवल एक थ्रेड का उपयोग कर सकती है। याद रखें कि सीरियल क्यू अन्य क्यू के संबंध में कंकरेंट हैं, इसलिए आपको प्रदर्शन लाभ मिलता है जब आप अपना काम एक क्यू पर ऑफलोड करते हैं, भले ही यह कंकरेंट न हो।

रेस कंडीशन

Swift Arrays, Dictionaries, Structs, और अन्य वैल्यू टाइप डिफ़ॉल्ट रूप से थ्रेड-सेफ नहीं हैं। उदाहरण के लिए, जब आपके पास एक ही सरणी को एक्सेस और संशोधित करने की कोशिश कर रहे कई थ्रेड्स हैं, तो आप मुसीबत में पड़ना शुरू कर देंगे।

रीडर्स-राइटर्स समस्या के अलग-अलग समाधान हैं, जैसे लॉक्स या सेमाफोर का उपयोग करना। लेकिन जिस प्रासंगिक समाधान पर मैं यहां चर्चा करना चाहता हूं वह है आइसोलेशन क्यू का उपयोग।

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

यह एक ट्रिकी समस्या है क्योंकि आपका ऐप 10 बार बिना किसी समस्या के चल सकता है, और फिर 11वीं बार क्रैश हो सकता है। इस स्थिति के लिए एक बहुत ही उपयोगी टूल Xcode में थ्रेड सैनिटाइज़र है। इस विकल्प को सक्षम करने से आपको अपने ऐप में संभावित रेस कंडीशन की पहचान करने में मदद मिलेगी।

iOS कंकरेंसी में मास्टरी: GCD क्यू और एक्ज़ीक्यूशन मोड को समझना
यह विकल्प केवल सिम्युलेटर पर उपलब्ध है

समस्या को प्रदर्शित करने के लिए, आइए इस (स्वीकार्य रूप से गढ़े गए) उदाहरण को लें:

class ViewController: UIViewController {
    let concurrent = DispatchQueue(label: "com.besher.concurrent", attributes: .concurrent)
    var array = [1,2,3,4,5]
    override func viewDidLoad() {
        for _ in 0...1 {
            race()
        }
    }
    func race() {
        concurrent.async {
            for i in self.array {
                // रीड एक्सेस
                print(i)
            }
        }
        concurrent.async {
            for i in 0..<10 {
                self.array.append(i) // राइट एक्सेस
            }
        }
    }
}

एक async टास्क मान जोड़कर सरणी को संशोधित कर रहा है। यदि आप इसे अपने सिम्युलेटर पर चलाने का प्रयास करते हैं, तो आप क्रैश नहीं कर सकते। लेकिन इसे पर्याप्त बार चलाएं (या लाइन 7 पर लूप फ्रीक्वेंसी बढ़ाएं), और आप अंततः क्रैश करेंगे। यदि आप थ्रेड सैनिटाइज़र सक्षम करते हैं, तो आपको हर बार ऐप चलाने पर चेतावनी मिलेगी।

iOS कंकरेंसी में मास्टरी: GCD क्यू और एक्ज़ीक्यूशन मोड को समझना

इस रेस कंडीशन से निपटने के लिए, हम एक आइसोलेशन क्यू जोड़ने जा रहे हैं जो बैरियर फ्लैग का उपयोग करती है। यह फ्लैग क्यू पर किसी भी बकाया टास्क को खत्म करने की अनुमति देता है, लेकिन बैरियर टास्क पूरा होने तक किसी भी आगे के टास्क को निष्पादित होने से रोकता है।

बैरियर को एक पब्लिक रेस्टरूम (शेयर्ड रिसोर्स) की सफाई करने वाले जेनिटर की तरह समझें। रेस्टरूम के अंदर कई (कंकरेंट) स्टॉल हैं जिनका लोग उपयोग कर सकते हैं।

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

अंत में जब पूरा हो जाता है, तो जेनिटर साइन (बैरियर) को हटा देता है ताकि बाहर कतार में खड़े लोग अंत में प्रवेश कर सकें।

यह कोड में ऐसा दिखता है:

class ViewController: UIViewController {
    let concurrent = DispatchQueue(label: "com.besher.concurrent", attributes: .concurrent)
    let isolation = DispatchQueue(label: "com.besher.isolation", attributes: .concurrent)
    private var _array = [1,2,3,4,5]
    var threadSafeArray: [Int] {
        get {
            return isolation.sync { _array }
        }
        set {
            isolation.async(flags: .barrier) {
                self._array = newValue
            }
        }
    }
    override func viewDidLoad() {
        for _ in 0...15 {
            race()
        }
    }
    func race() {
        concurrent.async {
            for i in self.threadSafeArray {
                print(i)
            }
        }
        concurrent.async {
            for i in 0..<10 {
                self.threadSafeArray.append(i)
            }
        }
    }
}

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

गेटर को मान सीधे वापस करने के लिए sync होना चाहिए। सेटर async हो सकता है, क्योंकि हमें राइट होने पर कॉलर को ब्लॉक करने की आवश्यकता नहीं है।

हम रेस कंडीशन को हल करने के लिए बैरियर के बिना एक सीरियल क्यू का उपयोग कर सकते थे, लेकिन फिर हम सरणी के लिए कंकरेंट रीड एक्सेस होने के लाभ को खो देंगे। शायद यह आपके मामले में समझ में आता है, आप तय कर सकते हैं।

निष्कर्ष

इतना दूर पढ़ने के लिए बहुत-बहुत धन्यवाद! मुझे आशा है कि आपने इस लेख से कुछ नया सीखा। मैं आपको एक सारांश और कुछ सामान्य सलाह के साथ छोड़ूंगा:

सारांश

  • क्यू हमेशा अपने टास्क को FIFO क्रम में शुरू करती हैं
  • क्यू हमेशा अन्य क्यू के सापेक्ष कंकरेंट होती हैं
  • सिंक बनाम एसिंक स्रोत को प्रभावित करता है
  • सीरियल बनाम कंकरेंट गंतव्य को प्रभावित करता है
  • सिंक 'ब्लॉकिंग' का पर्यायवाची है
  • एसिंक तुरंत कॉलर को कंट्रोल वापस करता है
  • सीरियल एक थ्रेड का उपयोग करती है, और निष्पादन के क्रम की गारंटी देती है
  • कंकरेंट मल्टीपल-थ्रेड्स का उपयोग करती है, और थ्रेड एक्सप्लोजन का जोखिम उठाती है
  • अपने डिज़ाइन साइकिल में कंकरेंसी के बारे में जल्दी सोचें
  • सिंक्रोनस कोड तर्क करना और डीबग करना आसान है
  • ग्लोबल कंकरेंट क्यू पर भरोसा करने से बचें यदि संभव हो
  • प्रति सबसिस्टम एक सीरियल क्यू से शुरू करने पर विचार करें
  • मापने योग्य प्रदर्शन लाभ केवल तभी कंकरेंट क्यू पर स्विच करें

मुझे Swift कंकरेंसी मैनिफेस्टो से 'कंकरेंसी के समुद्र में सीरियलाइजेशन का द्वीप' होने का रूपक पसंद है। इस भावना को Matt Diephouse के इस ट्वीट में भी साझा किया गया था:

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

यदि आपके कोई प्रश्न या टिप्पणी हैं, तो बेझिझक Twitter पर मुझसे संपर्क करें

Besher Al Maleh

कवर फोटो Unsplash पर Onur K द्वारा

साथी ऐप यहां डाउनलोड करें:

almaleh/Dispatcher - कंकरेंसी पर मेरे लेख के लिए साथी ऐप। GitHub पर अकाउंट बनाकर almaleh/Dispatcher विकास में योगदान दें।
iOS कंकरेंसी में मास्टरी: GCD क्यू और एक्ज़ीक्यूशन मोड को समझना iOS कंकरेंसी में मास्टरी: GCD क्यू और एक्ज़ीक्यूशन मोड को समझना

Fireworks — Swift के लिए एक विजुअल पार्टिकल एडिटर
मैकOS और iOS के लिए ऑन-द-फ्लाई Swift कोड जनरेट करें जैसे ही आप अपने पार्टिकल इफेक्ट्स को डिजाइन और इटरेट करते हैं
iOS कंकरेंसी में मास्टरी: GCD क्यू और एक्ज़ीक्यूशन मोड को समझना Besher Al Maleh - Flawless iOS

आपको (हमेशा) [weak self] की आवश्यकता नहीं है
इस लेख में, हम Swift क्लोजर के अंदर weak self के बारे में बात करेंगे रिटेन साइकिल से बचने के लिए और उन मामलों का पता लगाएंगे जहां self को weakly कैप्चर करना आवश्यक हो भी सकता है और नहीं भी।
iOS कंकरेंसी में मास्टरी: GCD क्यू और एक्ज़ीक्यूशन मोड को समझना Besher Al Maleh - Flawless iOS

iOS कंकरेंसी में मास्टरी: GCD क्यू और एक्ज़ीक्यूशन मोड को समझना

आगे पढ़ना:

परिचय
एक एप्लिकेशन में कंकरेंट कोड पथ को लागू करने का तरीका बताता है।
Concurrent Programming: APIs and Challenges · objc.io
objc.io iOS और OS X डेवलपमेंट के लिए उन्नत तकनीकों और प्रथाओं पर किताबें प्रकाशित करता है
iOS कंकरेंसी में मास्टरी: GCD क्यू और एक्ज़ीक्यूशन मोड को समझना Florian Kugler

Low-Level Concurrency APIs · objc.io
objc.io iOS और OS X डेवलपमेंट के लिए उन्नत तकनीकों और प्रथाओं पर किताबें प्रकाशित करता है
iOS कंकरेंसी में मास्टरी: GCD क्यू और एक्ज़ीक्यूशन मोड को समझना Daniel Eggert

https://khanlou.com/2016/04/the-GCD-handbook/

GCD में कंकरेंट बनाम सीरियल क्यू
मैं GCD में कंकरेंट और सीरियल क्यू को पूरी तरह से समझने के लिए संघर्ष कर रहा हूं। मेरे कुछ मुद्दे हैं और उम्मीद है कि कोई मुझे स्पष्ट रूप से और बिंदु पर जवाब दे सकता है। मैं पढ़ रहा हूं कि सीरियल क्यू बनाई जाती हैं...
iOS कंकरेंसी में मास्टरी: GCD क्यू और एक्ज़ीक्यूशन मोड को समझना Bogdan Alexandru - Stack Overflow

iOS कंकरेंसी में मास्टरी: GCD क्यू और एक्ज़ीक्यूशन मोड को समझना

WWDC वीडियो:

Modernizing Grand Central Dispatch Usage - WWDC 2017 - Videos - Apple Developer
macOS 10.13 और iOS 11 ने Grand Central Dispatch और Darwin कर्नेल के सहयोग को फिर से खोजा है, जिससे आपके एप्लिकेशन को चलाने में सक्षम बनाता है...
iOS कंकरेंसी में मास्टरी: GCD क्यू और एक्ज़ीक्यूशन मोड को समझना iOS कंकरेंसी में मास्टरी: GCD क्यू और एक्ज़ीक्यूशन मोड को समझना

Building Responsive and Efficient Apps with GCD - WWDC 2015 - Videos - Apple Developer
watchOS और iOS मल्टीटास्किंग आपके एप्लिकेशन की दक्षता और प्रतिक्रियाशीलता पर बढ़ती मांग रखते हैं। विशेषज्ञ मार्गदर्शन के साथ...
iOS कंकरेंसी में मास्टरी: GCD क्यू और एक्ज़ीक्यूशन मोड को समझना iOS कंकरेंसी में मास्टरी: GCD क्यू और एक्ज़ीक्यूशन मोड को समझना

  1. नए iOS 11 पॉडकास्ट ऐप में महारत हासिल करें: विशेषताएं और टिप्स नए iOS 11 पॉडकास्ट ऐप में महारत हासिल करें: विशेषताएं और टिप्स

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

  1. iOS 11 फोटोज ऐप में लोग जोड़ें या हटाएं: स्टेप-बाय-स्टेप गाइड iOS 11 फोटोज ऐप में लोग जोड़ें या हटाएं: स्टेप-बाय-स्टेप गाइड

    iOS 10 में Apple ने एक क्रांतिकारी फीचर पेश किया जो चेहरों को पहचानता है और उनका इस्तेमाल उन लोगों की तस्वीरें ऑर्गेनाइज करने के लिए करता है जिनके चेहरे उनमें हैं। तो जब भी आपको किसी प्रिय व्यक्ति की सभी तस्वीरें देखनी हों, सारी पिक्स पलटने के बजाय बस उस व्यक्ति की तस्वीर वाले थंबनेल पर क्लिक करना ह

  1. iOS 12 पर Messages में फोटो एक्सेस करना: चरण-दर-चरण गाइड iOS 12 पर Messages में फोटो एक्सेस करना: चरण-दर-चरण गाइड

    आप iOS 3 लॉन्च होने के बाद से Apple डिवाइस पर Messages के माध्यम से वीडियो और फोटो भेज सकते हैं। ऐसा करने के लिए आपको बस कैमरा आइकन पर टैप करना होगा और उन फोटो या वीडियो को चुनना होगा जिन्हें आप भेजना चाहते हैं। iOS 12 के साथ, चीजें बदल गई हैं। हालांकि, इसका मतलब यह नहीं है कि यह पूरी तरह से अक्षम ह