सबसे पहले, थ्रेड्स और कतारों के बीच अंतर जानना महत्वपूर्ण है और जीसीडी वास्तव में क्या करता है। जब हम प्रेषण कतारों (जीसीडी के माध्यम से) का उपयोग करते हैं, तो हम वास्तव में कतारबद्ध होते हैं, थ्रेडिंग नहीं। डिस्पैच ढांचे को विशेष रूप से हमें थ्रेडिंग से दूर करने के लिए डिज़ाइन किया गया था, क्योंकि 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