जीसीडी में समवर्ती बनाम सीरियल कतारें


117

मैं जीसीडी में समवर्ती और धारावाहिक कतारों को पूरी तरह से समझने के लिए संघर्ष कर रहा हूं। मेरे पास कुछ मुद्दे हैं और उम्मीद है कि कोई मुझे स्पष्ट रूप से और बिंदु पर जवाब दे सकता है।

  1. मैं पढ़ रहा हूं कि एक के बाद एक कार्यों को अंजाम देने के लिए सीरियल कतारों का निर्माण और उपयोग किया जाता है। हालांकि, अगर होता है:

    • मैं एक सीरियल कतार बनाता हूं
    • मैं dispatch_asyncतीन खंड ए, बी, सी को भेजने के लिए तीन बार (धारावाहिक कतार पर मैंने अभी बनाया) का उपयोग करता हूं

    क्या तीन खंड निष्पादित किए जाएंगे:

    • क्रम में A, B, C क्योंकि कतार सीरियल है

      या

    • समवर्ती (पैरलल थ्रेड्स पर एक ही समय में) क्योंकि मैं ASYNC प्रेषण का उपयोग करता था
  2. मैं पढ़ रहा हूं कि मैं dispatch_syncएक के बाद एक ब्लॉक निष्पादित करने के लिए समवर्ती कतारों पर उपयोग कर सकता हूं । उस मामले में, क्यों धारावाहिक कतारें मौजूद हैं, क्योंकि मैं हमेशा एक समवर्ती कतार का उपयोग कर सकता हूं जहां मैं जितना चाहे उतने ब्लॉक ब्लॉक कर सकता हूं?

    किसी भी अच्छी व्याख्या के लिए धन्यवाद!


एक साधारण अच्छा पूर्वापेक्षित प्रश्न असंक बनाम असमार्थक प्रेषण
हनी

जवाबों:


216

एक सरल उदाहरण: आपके पास एक ऐसा ब्लॉक है जिसे निष्पादित करने में एक मिनट लगता है। आप इसे मुख्य धागे से एक कतार में जोड़ते हैं। आइए चार मामलों को देखें।

  • async - समवर्ती: कोड एक पृष्ठभूमि थ्रेड पर चलता है। मुख्य थ्रेड (और UI) पर नियंत्रण तुरंत लौटाता है। ब्लॉक यह नहीं मान सकता कि यह उस कतार पर चलने वाला एकमात्र ब्लॉक है
  • async - धारावाहिक: कोड एक पृष्ठभूमि थ्रेड पर चलता है। नियंत्रण मुख्य धागे को तुरंत लौटाता है। ब्लॉक मान सकता है कि यह उस कतार पर चलने वाला एकमात्र ब्लॉक है
  • सिंक - समवर्ती: कोड एक पृष्ठभूमि थ्रेड पर चलता है लेकिन मुख्य थ्रेड यूआई के किसी भी अद्यतन को अवरुद्ध करने के लिए समाप्त होने की प्रतीक्षा करता है। ब्लॉक यह नहीं मान सकता कि यह उस कतार पर चलने वाला एकमात्र ब्लॉक है (मैं कुछ सेकंड पहले async का उपयोग करके एक और ब्लॉक जोड़ सकता था)
  • सिंक - सीरियल: कोड एक बैकग्राउंड थ्रेड पर चलता है लेकिन मुख्य थ्रेड इसके समाप्त होने का इंतजार करता है, यूआई के किसी भी अपडेट को ब्लॉक कर देता है। ब्लॉक मान सकता है कि यह उस कतार पर चलने वाला एकमात्र ब्लॉक है

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


14
तो आप मुझे बता रहे हैं कि: (1) कतार का प्रकार (संक्षिप्त या धारावाहिक) केवल तत्व है जो यह तय करता है कि कार्यों को क्रम में निष्पादित किया गया है या paralel में ;; (२) प्रेषण प्रकार (सिंक या एसिंक्स) केवल यह कह रहा है कि क्या निष्पादन अगले निर्देश पर जाता है या नहीं? मेरा मतलब है, अगर मैं किसी कार्य को भेजता हूं तो SYNC कोड तब तक ब्लॉक रहेगा जब तक कि कार्य पूरा नहीं हो जाता, फिर चाहे उस पर कोई भी कतार क्यों न लगाई जाए?
बोगडान अलेक्जेंड्रू

13
@BogdanAlexandru सही। कतार निष्पादन नीति तय करती है, न कि आप ब्लॉक को कैसे कतारबद्ध करते हैं। सिंक ब्लॉक के पूरा होने का इंतजार करता है, async नहीं।
Jano

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

2
@ पाब्लोआ।, मुख्य धागा एक सीरियल कतार है इसलिए वास्तव में केवल दो मामले हैं। इसके अलावा, यह बिल्कुल वैसा ही है। Async तुरंत लौटाता है (और वर्तमान रन लूप के अंत में ब्लॉक संभवत: निष्पादित हो जाता है)। मुख्य गोटा है यदि आप मुख्य धागे से मुख्य धागे से सिंक करते हैं , तो जिस स्थिति में आपको गतिरोध मिलता है।
स्टीफन डार्लिंगटन

1
@ShauketSheikh No. मुख्य धागा एक सीरियल कतार है, लेकिन सभी धारावाहिक मुख्य धागे नहीं हैं। चौथे बिंदु में, मुख्य धागा अवरुद्ध होगा, अपने काम को प्रतिस्पर्धा करने के लिए दूसरे धागे की प्रतीक्षा कर रहा है। यदि सीरियल कतार मुख्य धागा था तो आपको एक डेडलॉक मिलेगा।
स्टीफन डार्लिंगटन

122

यहाँ कुछ प्रयोगों के बारे में बताया गया है serial, concurrentजिनके बारे में मुझे यह समझने में मदद मिली है Grand Central Dispatch

 func doLongAsyncTaskInSerialQueue() {

   let serialQueue = DispatchQueue(label: "com.queue.Serial")
      for i in 1...5 {
        serialQueue.async {

            if Thread.isMainThread{
                print("task running in main thread")
            }else{
                print("task running in background thread")
            }
            let imgURL = URL(string: "https://upload.wikimedia.org/wikipedia/commons/0/07/Huge_ball_at_Vilnius_center.jpg")!
            let _ = try! Data(contentsOf: imgURL)
            print("\(i) completed downloading")
        }
    }
}

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

func doLongSyncTaskInSerialQueue() {
    let serialQueue = DispatchQueue(label: "com.queue.Serial")
    for i in 1...5 {
        serialQueue.sync {
            if Thread.isMainThread{
                print("task running in main thread")
            }else{
                print("task running in background thread")
            }
            let imgURL = URL(string: "https://upload.wikimedia.org/wikipedia/commons/0/07/Huge_ball_at_Vilnius_center.jpg")!
            let _ = try! Data(contentsOf: imgURL)
            print("\(i) completed downloading")
        }
    }
}

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

func doLongASyncTaskInConcurrentQueue() {
    let concurrentQueue = DispatchQueue(label: "com.queue.Concurrent", attributes: .concurrent)
    for i in 1...5 {
        concurrentQueue.async {
            if Thread.isMainThread{
                print("task running in main thread")
            }else{
                print("task running in background thread")
            }
            let imgURL = URL(string: "https://upload.wikimedia.org/wikipedia/commons/0/07/Huge_ball_at_Vilnius_center.jpg")!
            let _ = try! Data(contentsOf: imgURL)
            print("\(i) completed downloading")
        }
        print("\(i) executing")
    }
}

जब आप GCD में async का उपयोग करेंगे तो बैकग्राउंड थ्रेड में टास्क चलेगा। Async का अर्थ है अगली पंक्ति को निष्पादित करें जब तक कि ब्लॉक निष्पादित नहीं करता है तब तक प्रतीक्षा न करें, जिसके परिणामस्वरूप गैर अवरुद्ध मुख्य धागा होता है। समवर्ती कतार में याद रखें, कार्य को उस क्रम में संसाधित किया जाता है जिसे वे कतार में जोड़ते हैं लेकिन कतार से जुड़े विभिन्न थ्रेड्स के साथ। याद रखें कि वे कार्य को समाप्त करने के लिए नहीं माना जाता है क्योंकि उन्हें कतार में जोड़ा जाता है। कार्य के अलग-अलग होने पर प्रत्येक बार थ्रेड्स आवश्यक रूप से स्वचालित रूप से बनाए जाते हैं। टास्क को समानांतर में निष्पादित किया जाता है। इससे अधिक (maxConcurrentOperationCount) तक पहुँचने के बाद, कुछ कार्य धारावाहिक के रूप में तब तक व्यवहार करेंगे जब तक कि एक धागा मुक्त न हो जाए।

func doLongSyncTaskInConcurrentQueue() {
  let concurrentQueue = DispatchQueue(label: "com.queue.Concurrent", attributes: .concurrent)
    for i in 1...5 {
        concurrentQueue.sync {
            if Thread.isMainThread{
                print("task running in main thread")
            }else{
                print("task running in background thread")
            }
            let imgURL = URL(string: "https://upload.wikimedia.org/wikipedia/commons/0/07/Huge_ball_at_Vilnius_center.jpg")!
            let _ = try! Data(contentsOf: imgURL)
            print("\(i) completed downloading")
        }
        print("\(i) executed")
    }
}

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

इन प्रयोगों का सारांश इस प्रकार है

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

यूआई संबंधित कार्य हमेशा मुख्य पंक्ति से कार्य को मुख्य कतार में भेजकर DispatchQueue.main.sync/asyncकिया जाना चाहिए। हाथ की उपयोगिता है, जबकि नेटवर्क संबंधी / भारी संचालन को हमेशा अतुल्यकालिक रूप से किया जाना चाहिए कोई भी मामला जो कभी भी आप मुख्य या पृष्ठभूमि का उपयोग कर रहे हैं।

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

func doMultipleSyncTaskWithinAsynchronousOperation() {
    let concurrentQueue = DispatchQueue(label: "com.queue.Concurrent", attributes: .concurrent)
    concurrentQueue.async {
        let concurrentQueue = DispatchQueue.global(qos: DispatchQoS.QoSClass.default)
        for i in 1...5 {
            concurrentQueue.sync {
                let imgURL = URL(string: "https://upload.wikimedia.org/wikipedia/commons/0/07/Huge_ball_at_Vilnius_center.jpg")!
                let _ = try! Data(contentsOf: imgURL)
                print("\(i) completed downloading")
            }
            print("\(i) executed")
        }
    }
}

EDIT EDIT: आप यहां डेमो वीडियो देख सकते हैं


शानदार प्रदर्शन .... अगली पंक्ति तब तक प्रतीक्षा न करें जब तक कि ब्लॉक निष्पादित नहीं करता है जिसके परिणामस्वरूप गैर अवरुद्ध मुख्य धागा होता है यही कारण है कि यदि आप पृष्ठभूमि थ्रेड पर ब्रेकपॉइंट का उपयोग करते हैं तो यह कूद जाएगा }क्योंकि यह वास्तव में उस क्षण निष्पादित नहीं कर रहा है
हनी

@ लाज आईओएस गाई 웃 मैं अभी भी async समवर्ती और async धारावाहिक के बीच का अंतर नहीं समझता। या तो उपयोग करने का निहितार्थ क्या है। वे दोनों पृष्ठभूमि में यूआई को परेशान नहीं करते हैं। और आप कभी सिंक का उपयोग क्यों करेंगे? सभी कोड सिंक नहीं है। एक के बाद एक?
eonist


@ वह आलसी iOS गाय 웃: कि बनाने के लिए thx। मैंने सुस्त स्विफ्ट-लैंग पर पोस्ट किया। Would होगा यदि आप डिस्पैचग्रुप और डिस्पैचवर्क इटेम के बारे में एक बना सकते हैं। : D
eonist

मैं अपने पिछले एक, का परीक्षण किया है concurrentQueue.syncकी doLongSyncTaskInConcurrentQueue(), समारोह यह, मुख्य थ्रेड प्रिंट Task will run in different threadसच नहीं लगता है।
गबलर

54

सबसे पहले, थ्रेड्स और कतारों के बीच अंतर जानना महत्वपूर्ण है और जीसीडी वास्तव में क्या करता है। जब हम प्रेषण कतारों (जीसीडी के माध्यम से) का उपयोग करते हैं, तो हम वास्तव में कतारबद्ध होते हैं, थ्रेडिंग नहीं। डिस्पैच ढांचे को विशेष रूप से हमें थ्रेडिंग से दूर करने के लिए डिज़ाइन किया गया था, क्योंकि Apple स्वीकार करता है कि "एक सही थ्रेडिंग समाधान को लागू करना [कर सकते हैं] अत्यंत कठिन हो जाता है, अगर [कभी-कभी] असंभव को प्राप्त करना असंभव नहीं है।" इसलिए, समवर्ती कार्यों को करने के लिए (ऐसे कार्य जिन्हें हम यूआई को फ्रीज़ नहीं करना चाहते हैं), हम सभी को उन कार्यों की एक कतार बनाने और इसे GCD को सौंपने की आवश्यकता है। और जीसीडी संबंधित थ्रेडिंग के सभी संभालती है। इसलिए, हम वास्तव में कर रहे हैं सब कतार है।

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

serialQueue.async {
    // this is one task
    // it can be any number of lines with any number of methods
}
serialQueue.async {
    // this is another task added to the same queue
    // this queue now has two tasks
}

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

दो प्रकार की कतारें हैं, धारावाहिक और समवर्ती, लेकिन सभी कतारें एक दूसरे के सापेक्ष समवर्ती हैं । तथ्य यह है कि आप किसी भी कोड को "पृष्ठभूमि में" चलाना चाहते हैं इसका मतलब है कि आप इसे दूसरे धागे (आमतौर पर मुख्य धागा) के साथ समवर्ती रूप से चलाना चाहते हैं। इसलिए, सभी प्रेषण कतारों, धारावाहिक या समवर्ती, अन्य कतारों के सापेक्ष अपने कार्यों को समवर्ती रूप से निष्पादित करते हैं । कतारों द्वारा (धारावाहिक कतारों द्वारा) कोई भी धारावाहिकीकरण, केवल उस एकल [धारावाहिक] प्रेषण कतार के भीतर के कार्यों के साथ करना है (जैसे ऊपर दिए गए उदाहरण में जहां एक ही धारावाहिक कतार के भीतर दो कार्य हैं, उन कार्यों को एक के बाद एक निष्पादित किया जाएगा; अन्य, कभी भी एक साथ नहीं)।

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

let serialQueue = DispatchQueue(label: "serial")

आप इसकी विशेषता संपत्ति के माध्यम से समवर्ती बना सकते हैं:

let concurrentQueue = DispatchQueue(label: "concurrent", attributes: [.concurrent])

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

कॉनकॉरेंट प्रश्न (अक्सर वैश्विक प्रेषण कतारों के रूप में जाना जाता है) एक साथ कार्यों को निष्पादित कर सकते हैं; हालांकि, कार्यों को उस विशिष्ट कतार में जोड़ने के लिए आदेश में आरंभ करने की गारंटी दी जाती है, लेकिन धारावाहिक कतारों के विपरीत, कतार दूसरे कार्य को शुरू करने से पहले समाप्त होने के लिए पहले कार्य की प्रतीक्षा नहीं करती है। कार्य (धारावाहिक कतारों के साथ) अलग-अलग धागों पर चलते हैं और (धारावाहिक कतारों के साथ) प्रत्येक कार्य को एक ही धागे पर चलाने की गारंटी नहीं है (महत्वपूर्ण नहीं, लेकिन जानना दिलचस्प है)। और आईओएस फ्रेमवर्क चार तैयार-से-उपयोग के समवर्ती कतारों के साथ आता है। आप उपरोक्त उदाहरण का उपयोग करके या Apple की वैश्विक कतारों में से एक का उपयोग करके एक समवर्ती कतार बना सकते हैं (जो आमतौर पर अनुशंसित है):

let concurrentQueue = DispatchQueue.global(qos: .default)

RETAIN-CYCLE RESISTANT: डिस्पैच कतारें संदर्भ-गणना की गई वस्तुएं हैं लेकिन आपको वैश्विक कतारों को बनाए रखने और जारी करने की आवश्यकता नहीं है क्योंकि वे वैश्विक हैं, और इस प्रकार बनाए रखती हैं और रिलीज़ को अनदेखा किया जाता है। आप उन्हें एक संपत्ति के लिए आवंटित किए बिना सीधे वैश्विक कतारों का उपयोग कर सकते हैं।

कतारों को भेजने के दो तरीके हैं: तुल्यकालिक और अतुल्यकालिक।

SYNC DISPATCHING का अर्थ है कि वह कतार जहाँ कतार को भेजा गया था (कॉलिंग धागा) कतार को भेजने के बाद रुक जाता है और फिर से शुरू करने से पहले उस कतार खंड में कार्य का इंतजार करता है। सिंक्रोनाइज़ करने के लिए:

DispatchQueue.global(qos: .default).sync {
    // task goes in here
}

ASYNC DISPATCHING का अर्थ है कि कतार को भेजने के बाद कॉलिंग थ्रेड चलना जारी रहता है और निष्पादन समाप्त करने के लिए उस कतार ब्लॉक में कार्य का इंतजार नहीं करता है। अतुल्यकालिक रूप से भेजने के लिए:

DispatchQueue.global(qos: .default).async {
    // task goes in here
}

अब कोई सोच सकता है कि धारावाहिक में किसी कार्य को अंजाम देने के लिए, एक सीरियल कतार का उपयोग किया जाना चाहिए, और यह बिल्कुल सही नहीं है। धारावाहिक में कई कार्यों को निष्पादित करने के लिए , एक सीरियल कतार का उपयोग किया जाना चाहिए, लेकिन सभी कार्यों को (स्वयं के द्वारा अलग) सीरियल में निष्पादित किया जाता है। इस उदाहरण पर विचार करें:

whichQueueShouldIUse.syncOrAsync {
    for i in 1...10 {
        print(i)
    }
    for i in 1...10 {
        print(i + 100)
    }
    for i in 1...10 {
        print(i + 1000)
    }
}

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

इन दो कतारों पर विचार करें, एक धारावाहिक और एक समवर्ती:

let serialQueue = DispatchQueue(label: "serial")
let concurrentQueue = DispatchQueue.global(qos: .default)

कहो कि हम दो समवर्ती कतारों को async में भेजते हैं:

concurrentQueue.async {
    for i in 1...5 {
        print(i)
    }
}
concurrentQueue.async {
    for i in 1...5 {
        print(i + 100)
    }
}

1
101
2
102
103
3
104
4
105
5

उनका आउटपुट जंप किया गया है (जैसा कि अपेक्षित है) लेकिन ध्यान दें कि प्रत्येक कतार ने सीरियल में अपने स्वयं के कार्य को निष्पादित किया। यह संगामिति का सबसे मूल उदाहरण है - एक ही कतार में पृष्ठभूमि में एक ही समय में दो कार्य चलना। चलिए अब पहला पहला धारावाहिक बनाते हैं:

serialQueue.async {
    for i in 1...5 {
        print(i)
    }
}
concurrentQueue.async {
    for i in 1...5 {
        print(i + 100)
    }
}

101
1
2
102
3
103
4
104
5
105

क्या धारावाहिक में पहली कतार को नहीं माना जाता है? यह (और इसलिए दूसरा था)। पृष्ठभूमि में जो कुछ भी हुआ वह कतार के लिए कोई चिंता का विषय नहीं है। हमने सीरियल कतार को धारावाहिक में निष्पादित करने के लिए कहा और यह किया ... लेकिन हमने केवल इसे एक काम दिया। अब इसे दो कार्य देते हैं:

serialQueue.async {
    for i in 1...5 {
        print(i)
    }
}
serialQueue.async {
    for i in 1...5 {
        print(i + 100)
    }
}

1
2
3
4
5
101
102
103
104
105

और यह क्रमबद्धता का सबसे बुनियादी (और केवल संभव) उदाहरण है - एक ही कतार में पृष्ठभूमि में (मुख्य धागे के लिए) धारावाहिक (एक के बाद एक) में चल रहे दो कार्य। लेकिन अगर हमने उन्हें दो अलग-अलग धारावाहिक कतारें बनायीं (क्योंकि ऊपर के उदाहरण में वे एक ही कतार हैं), तो उनका आउटपुट उछाला जाता है:

serialQueue.async {
    for i in 1...5 {
        print(i)
    }
}
serialQueue2.async {
    for i in 1...5 {
        print(i + 100)
    }
}

1
101
2
102
3
103
4
104
5
105

और यही मेरा मतलब है जब मैंने कहा कि सभी कतारें एक दूसरे के सापेक्ष समवर्ती हैं। ये एक ही समय में अपने कार्यों को अंजाम देने वाली दो सीरियल कतारें हैं (क्योंकि वे अलग-अलग कतारें हैं)। एक कतार अन्य कतारों के बारे में नहीं जानती या परवाह नहीं करती है। अब दो धारावाहिक कतारों (एक ही कतार) में वापस जाते हैं और एक तीसरी पंक्ति, एक समवर्ती जोड़ते हैं:

serialQueue.async {
    for i in 1...5 {
        print(i)
    }
}
serialQueue.async {
    for i in 1...5 {
        print(i + 100)
    }
}
concurrentQueue.async {
    for i in 1...5 {
        print(i + 1000)
    }
}

1
2
3
4
5
101
102
103
104
105
1001
1002
1003
1004
1005

यह एक तरह से अप्रत्याशित है, समवर्ती कतार धारावाहिक कतारों की प्रतीक्षा करने से पहले ही समाप्त हो गई? यह संक्षिप्त नहीं है। आपका खेल का मैदान एक अलग आउटपुट दिखा सकता है, लेकिन मेरा यह दिखाया गया है। और यह दिखाया क्योंकि मेरी समवर्ती कतार की प्राथमिकता जीसीडी के लिए अपने कार्य को जल्द से जल्द पूरा करने के लिए पर्याप्त नहीं थी। इसलिए यदि मैं सब कुछ समान रखता हूं लेकिन वैश्विक कतार की क्यूओएस (इसकी सेवा की गुणवत्ता, जो कि केवल कतार का प्राथमिकता स्तर है) को बदल देता है let concurrentQueue = DispatchQueue.global(qos: .userInteractive), तो आउटपुट अपेक्षित है:

1
1001
1002
1003
2
1004
1005
3
4
5
101
102
103
104
105

धारावाहिक में दो धारावाहिक कतारों ने अपने कार्यों को अंजाम दिया (उम्मीद के मुताबिक) और समवर्ती कतार ने अपने कार्य को तेजी से निष्पादित किया क्योंकि इसे उच्च प्राथमिकता स्तर (एक उच्च क्यूओएस, या सेवा की गुणवत्ता) दिया गया था।

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

concurrentQueue.sync {
    for i in 1...5 {
        print(i)
    }
}
concurrentQueue.async {
    for i in 1...5 {
        print(i + 100)
    }
}

1
2
3
4
5
101
102
103
104
105

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

और यह इस कारण से है कि हम निम्नलिखित कार्य नहीं कर सकते हैं:

DispatchQueue.main.sync { ... }

यह कतारों और प्रेषण विधियों का एकमात्र संभव संयोजन है जिसे हम मुख्य कतार पर समकालिक प्रेषण नहीं कर सकते हैं। और ऐसा इसलिए है क्योंकि हम मुख्य कतार को तब तक जमने के लिए कह रहे हैं जब तक हम घुंघराले ब्रेस के भीतर कार्य को अंजाम नहीं दे देते ... जिसे हमने मुख्य कतार में भेज दिया, जिसे हमने अभी-अभी फ्रीज किया है। इसे गतिरोध कहा जाता है। खेल के मैदान में इसे देखने के लिए:

DispatchQueue.main.sync { // stop the main queue and wait for the following to finish
    print("hello world") // this will never execute on the main queue because we just stopped it
}
// deadlock

एक अंतिम बात संसाधनों का उल्लेख है। जब हम एक कतार को एक कार्य देते हैं, तो जीसीडी अपने आंतरिक रूप से प्रबंधित पूल से एक उपलब्ध कतार ढूंढता है। जहाँ तक इस उत्तर के लेखन की बात है, वहाँ 64 कतारें प्रति qos उपलब्ध हैं। यह बहुत कुछ लग सकता है लेकिन वे जल्दी से खाए जा सकते हैं, खासकर तीसरे पक्ष के पुस्तकालयों, विशेष रूप से डेटाबेस फ्रेमवर्क द्वारा। इस कारण से, Apple के पास कतार प्रबंधन (नीचे दिए गए लिंक में उल्लिखित) के बारे में सिफारिशें हैं; एक जा रहा है:

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

ऐसा करने के लिए, उन्हें बनाने के बजाय जैसा हमने पहले किया था (जो आप अभी भी कर सकते हैं), Apple इस तरह धारावाहिक कतारें बनाने की सिफारिश करता है:

let serialQueue = DispatchQueue(label: "serialQueue", qos: .default, attributes: [], autoreleaseFrequency: .inherit, target: .global(qos: .default))

आगे पढ़ने के लिए, मैं निम्नलिखित सलाह देता हूं:

https://developer.apple.com/library/archive/documentation/General/Conceptual/ConcurrencyProgrammingGuide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40008091-CH1-SW1

https://developer.apple.com/documentation/dispatch/dispatchqueue


7

अगर मैं सही तरीके से समझता हूं कि जीसीडी कैसे काम करता है, मुझे लगता है कि दो प्रकार के होते हैं DispatchQueue, serialऔर concurrent, एक ही समय में, दो तरीके हैं कि DispatchQueueइसके कार्यों को कैसे भेजा जाए, असाइन किया गया है closure, पहला है async, और दूसरा है sync। वे एक साथ निर्धारित करते हैं कि वास्तव में बंद (कार्य) कैसे निष्पादित किया जाता है।

मैंने पाया serialऔर concurrentइसका मतलब है कि कतार के कितने धागे उपयोग कर सकते हैं, serialएक का मतलब है, जबकि concurrentकई का मतलब है। और syncऔर asyncइसका अर्थ है कि कार्य को किस थ्रेड पर निष्पादित किया जाएगा, कॉलर का धागा या उस कतार में अंतर्निहित धागा, का syncअर्थ कॉलर के धागे asyncपर चलता है जबकि अंतर्निहित धागे पर चलता है।

निम्नलिखित प्रयोगात्मक कोड है जो Xcode खेल के मैदान पर चल सकता है।

PlaygroundPage.current.needsIndefiniteExecution = true
let cq = DispatchQueue(label: "concurrent.queue", attributes: .concurrent)
let cq2 = DispatchQueue(label: "concurent.queue2", attributes: .concurrent)
let sq = DispatchQueue(label: "serial.queue")

func codeFragment() {
  print("code Fragment begin")
  print("Task Thread:\(Thread.current.description)")
  let imgURL = URL(string: "http://stackoverflow.com/questions/24058336/how-do-i-run-asynchronous-callbacks-in-playground")!
  let _ = try! Data(contentsOf: imgURL)
  print("code Fragment completed")
}

func serialQueueSync() { sq.sync { codeFragment() } }
func serialQueueAsync() { sq.async { codeFragment() } }
func concurrentQueueSync() { cq2.sync { codeFragment() } }
func concurrentQueueAsync() { cq2.async { codeFragment() } }

func tasksExecution() {
  (1...5).forEach { (_) in
    /// Using an concurrent queue to simulate concurent task executions.
    cq.async {
      print("Caller Thread:\(Thread.current.description)")
      /// Serial Queue Async, tasks run serially, because only one thread that can be used by serial queue, the underlying thread of serial queue.
      //serialQueueAsync()
      /// Serial Queue Sync, tasks run serially, because only one thread that can be used by serial queue,one by one of the callers' threads.
      //serialQueueSync()
      /// Concurrent Queue Async, tasks run concurrently, because tasks can run on different underlying threads
      //concurrentQueueAsync()
      /// Concurrent Queue Sync, tasks run concurrently, because tasks can run on different callers' thread
      //concurrentQueueSync()
    }
  }
}
tasksExecution()

आशा है कि यह मददगार हो सकता है।


7

मुझे इस रूपक का उपयोग करना अच्छा लगता है (यहाँ मूल छवि का लिंक है):

पिताजी की कुछ मदद चाहिए

चलो कल्पना करते हैं कि आपके पिताजी व्यंजन कर रहे हैं और आपके पास बस एक गिलास सोडा है। आप अपने डैड को साफ करने के लिए ग्लास लाते हैं, दूसरे डिश के अलावा डालते हैं।

अब आपके पिताजी स्वयं सभी व्यंजन बना रहे हैं, इसलिए वह उन्हें एक-एक करके करने जा रहा है: यहाँ आपके पिताजी एक सीरियल कतार का प्रतिनिधित्व करते हैं ।

लेकिन आप वास्तव में वहां खड़े होने और इसे साफ करने के लिए देखने के इच्छुक नहीं हैं। तो, आप ग्लास को गिराते हैं, और अपने कमरे में वापस जाते हैं: इसे एक एस्किंस प्रेषण कहा जाता है । आपके पिताजी एक बार करवाने के बाद आपको बता सकते हैं या नहीं दे सकते हैं, लेकिन महत्वपूर्ण बात यह है कि आप कांच के साफ होने का इंतजार नहीं कर रहे हैं; आप अपने कमरे में वापस जाते हैं, आप जानते हैं, बच्चे सामान।

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

और अंत में मान लें कि आपकी माँ आपके पिताजी की मदद करने का फैसला करती है और व्यंजन बनाने में उनका साथ देती है। अब कतार एक समवर्ती कतार बन जाती है क्योंकि वे एक ही समय में कई व्यंजन साफ ​​कर सकते हैं; लेकिन ध्यान दें कि आप अभी भी वहां इंतजार करने का फैसला कर सकते हैं या अपने कमरे में वापस जा सकते हैं, भले ही वे कैसे भी काम करें।

उम्मीद है की यह मदद करेगा


3

1. मैं पढ़ रहा हूं कि एक के बाद एक कार्यों को अंजाम देने के लिए सीरियल कतारों का निर्माण और उपयोग किया जाता है। हालाँकि, क्या होता है अगर: - • मैं एक सीरियल कतार बनाता हूं • मैं तीन ब्लॉक ए, बी, सी को भेजने के लिए तीन बार डिस्पैच_संक्यू (सीरियल कतार मैं अभी बनाया गया) का उपयोग करता हूं

उत्तर : - सभी तीन ब्लॉक एक के बाद एक निष्पादित होते हैं। मैंने एक नमूना कोड बनाया है जो समझने में मदद करता है।

let serialQueue = DispatchQueue(label: "SampleSerialQueue")
//Block first
serialQueue.async {
    for i in 1...10{
        print("Serial - First operation",i)
    }
}

//Block second
serialQueue.async {
    for i in 1...10{
        print("Serial - Second operation",i)
    }
}
//Block Third
serialQueue.async {
    for i in 1...10{
        print("Serial - Third operation",i)
    }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.