हर मोबाइल एप्लिकेशन एक साथ कई कार्य करता है: नेटवर्क से डेटा लोड करना, उपयोगकर्ता के स्पर्श को प्रोसेस करना, इंटरफ़ेस को एनिमेट करना और फ़ाइलों को सहेजना। यदि यह सारा कोड एक ही थ्रेड में चलता है, तो किसी भी नेटवर्क देरी पर एप्लिकेशन हैंग हो जाता है। मल्टीथ्रेडिंग और कंकरेंसी प्रमुख अवधारणाएँ हैं जो एप्लिकेशन को प्रतिक्रियाशील और कुशल बने रहने देती हैं। इस लेख में हम सभी मुख्य टूल्स को कवर करेंगे: Main Thread और RunLoop से लेकर Kotlin कोरूटीन और iOS पर Combine तक। सामग्री Apple की आधिकारिक GCD दस्तावेज़ीकरण पर आधारित है।
मुख्य बिंदु
मल्टीथ्रेडिंग एक एप्लिकेशन की एक साथ कोड के कई टुकड़ों को निष्पादित करने की क्षमता है। प्रत्येक टुकड़ा एक अलग थ्रेड (Thread) में चलता है — अपने स्वयं के कॉल स्टैक के साथ एक हल्की प्रक्रिया। मोबाइल डेवलपमेंट में, थ्रेड दो श्रेणियों में विभाजित होते हैं: Main Thread (UI थ्रेड) और Background Threads (पृष्ठभूमि थ्रेड)।
ऑपरेटिंग सिस्टम स्वयं प्रोसेसर कोर में थ्रेड के वितरण का प्रबंधन करता है। आधुनिक उपकरणों में 6–8 कोर होते हैं, इसलिए समानांतर निष्पादन वास्तव में काम को गति दे सकता है। हालाँकि, थ्रेड बनाना एक महँगा ऑपरेशन है, इसलिए सीधे Thread के साथ काम करने की अनुशंसा नहीं की जाती है। इसके बजाय, उच्च-स्तरीय अब्स्ट्रैक्शन का उपयोग किया जाता है: DispatchQueue, OperationQueue, CoroutineDispatcher।
कंकरेंसी मल्टीथ्रेडिंग की तुलना में एक व्यापक अवधारणा है। कंकरेंसी का अर्थ है कि कार्य कॉन्टेक्स्ट स्विचिंग के माध्यम से एक कोर पर भी "एक साथ" निष्पादित हो सकते हैं। एसिंक्रोनसी (Async/Await) एक प्रोग्रामिंग मॉडल है जहाँ कोई कार्य थ्रेड को ब्लॉक नहीं करता है, बल्कि परिणाम की प्रतीक्षा करते हुए नियंत्रण वापस करता है। आधुनिक भाषाओं (Kotlin, Swift, Dart) में अंतर्निहित Async/Await समर्थन है।
IT Sectr में, हम परियोजना की शुरुआत में उचित मल्टीथ्रेडिंग आर्किटेक्चर पर विशेष ध्यान देते हैं। प्रारंभिक चरण में की गई गलतियाँ पकड़ने में मुश्किल बग्स का कारण बनती हैं: डेटा रेस, डेडलॉक और लोड के तहत एप्लिकेशन की अस्थिरता। हमारी प्रत्येक परियोजना योजना चरण में कंकरेंसी आर्किटेक्चर समीक्षा से गुज़रती है।
Main Thread (मुख्य थ्रेड) — मोबाइल एप्लिकेशन में एकमात्र थ्रेड जिसकी UI तक पहुँच है। Android पर इसे UI Thread कहा जाता है, iOS पर — Main Thread। सभी इंटरफ़ेस संचालन — टेक्स्ट बदलना, एनिमेशन, स्पर्श प्रसंस्करण — केवल Main Thread पर किए जाते हैं। यदि मुख्य थ्रेड पर कोई भारी ऑपरेशन (फ़ाइल लोड करना, JSON पार्सिंग) किया जाता है, तो इंटरफ़ेस प्रतिक्रिया देना बंद कर देता है। Android पर इससे ANR (Application Not Responding) होता है, iOS पर — स्क्रीन "फ़्रीज़" हो जाती है।
Background Threads (पृष्ठभूमि थ्रेड) UI से संबंधित न होने वाली हर चीज़ के लिए हैं: नेटवर्क अनुरोध, डेटाबेस संचालन, इमेज प्रोसेसिंग, क्रिप्टोग्राफी। पूरा होने के बाद, परिणाम प्रदर्शन के लिए Main Thread पर भेजा जाता है। प्रत्येक प्लेटफ़ॉर्म थ्रेड के बीच स्विच करने के लिए अपने स्वयं के टूल प्रदान करता है: iOS में DispatchQueue.main.async, Android में runOnUiThread या withContext(Dispatchers.Main)।
RunLoop — iOS मुख्य थ्रेड पर ईवेंट प्रोसेसिंग लूप। RunLoop ईवेंट (स्पर्श, टाइमर, सूचनाएँ) की प्रतीक्षा करता है और उन्हें उपयुक्त हैंडलर को भेजता है। Android पर समकक्ष Looper है, जो प्रत्येक Main Thread से जुड़ा होता है। Main Looper कतार से अनंत रूप से संदेश निकालता है और प्रसंस्करण के लिए Handler को भेजता है। RunLoop और Looper को समझने से मेमोरी लीक और इंटरफ़ेस "हकलाने" से बचने में मदद मिलती है।
Grand Central Dispatch (GCD) — C भाषा स्तर पर मल्टीथ्रेडिंग प्रबंधन के लिए Apple लाइब्रेरी। GCD DispatchQueue — कार्य कतारों के साथ काम करता है। डेवलपर मैन्युअल रूप से थ्रेड नहीं बनाता है; GCD एक थ्रेड पूल का प्रबंधन करता है, उपलब्ध प्रोसेसर कोर में कार्यों को वितरित करता है। DispatchQueue दो प्रकार के होते हैं: Serial Queue (अनुक्रमिक — कार्य एक के बाद एक निष्पादित होते हैं) और Concurrent Queue (समानांतर — कार्य एक साथ निष्पादित हो सकते हैं)।
Main DispatchQueue — मुख्य थ्रेड से बंधी एक अनुक्रमिक कतार है। Global Queues — विभिन्न प्राथमिकताओं (QoS — Quality of Service) वाली समानांतर कतारें: userInteractive, userInitiated, utility, background। सही QoS चुनना प्रदर्शन के लिए महत्वपूर्ण है: .userInteractive — UI को प्रभावित करने वाले कार्यों के लिए (एनिमेशन, रेंडरिंग); .background — समय-गैर-महत्वपूर्ण कार्यों के लिए (सिंक्रोनाइज़ेशन, कैश सफाई)।
OperationQueue — अतिरिक्त क्षमताओं के साथ GCD के ऊपर एक अब्स्ट्रैक्शन: कार्य रद्द करना, संचालन के बीच निर्भरताएँ सेट करना, समानांतर संचालन की अधिकतम संख्या को नियंत्रित करना। संचालन — Operation क्लास (या BlockOperation) की वस्तुएँ हैं। उदाहरण: यदि आपको एक छवि लोड करनी है, फिर एक फ़िल्टर लागू करना है और उसके बाद ही इसे प्रदर्शित करना है — OperationQueue निर्भरताओं के साथ इसे पूरी तरह से संभालता है। GCD में आपको DispatchGroup या सेमाफ़ोर का उपयोग करके इन चरणों को मैन्युअल रूप से सिंक्रोनाइज़ करना होता।
Swift 5.5+ में Async/Await — GCD का एक आधुनिक विकल्प है। कीवर्ड async और await एसिंक्रोनस कोड को रैखिक और पढ़ने योग्य बनाते हैं। फ़ंक्शन async के रूप में चिह्नित किए जाते हैं, और कॉल await के माध्यम से प्रतीक्षित किए जाते हैं। सिस्टम स्वयं कॉन्टेक्स्ट स्विचिंग का प्रबंधन करता है: डिफ़ॉल्ट रूप से, एक async फ़ंक्शन पृष्ठभूमि थ्रेड पर निष्पादित होता है, जबकि UI अपडेट MainActor पर निष्पादित होता है। @MainActor — एक विशेषता है जो मुख्य थ्रेड पर कोड निष्पादन की गारंटी देती है।
Coroutines (कोरूटीन) — JetBrains द्वारा विकसित Kotlin के लिए हल्के थ्रेड हैं। सामान्य थ्रेड के विपरीत, कोरूटीन किसी विशिष्ट Thread से बंधे नहीं होते हैं। हज़ारों कोरूटीन महत्वपूर्ण ओवरहेड के बिना कई थ्रेड पर चल सकते हैं। CoroutineScope कोरूटीन के जीवनचक्र का प्रबंधन करता है: viewModelScope ViewModel से बंधा होता है, lifecycleScope — Activity/Fragment से। जब स्कोप नष्ट हो जाता है, तो सभी चाइल्ड कोरूटीन स्वचालित रूप से रद्द हो जाते हैं।
Dispatchers यह निर्धारित करते हैं कि कोरूटीन किस थ्रेड पूल पर चलता है: Dispatchers.Main — UI थ्रेड; Dispatchers.IO — नेटवर्क अनुरोधों और डिस्क संचालन के लिए; Dispatchers.Default — CPU-गहन गणनाओं के लिए। डिस्पैचर स्विच करने के लिए withContext का उपयोग किया जाता है। कोरूटीन स्ट्रक्चर्ड कंकरेंसी का समर्थन करते हैं: प्रत्येक कोरूटीन का एक पैरेंट होता है, और जब पैरेंट रद्द होता है तो सभी चाइल्ड कोरूटीन रद्द हो जाते हैं। यह मेमोरी लीक और लटकते कार्यों को रोकता है।
Flow — कोरूटीन लाइब्रेरी से एक कोल्ड एसिंक्रोनस डेटा स्ट्रीम। Flow मानों को अनुक्रमिक रूप से उत्सर्जित करता है: (1) प्रोड्यूसर डेटा उत्पन्न करता है, (2) ऑपरेटर स्ट्रीम को बदलते हैं, (3) कलेक्टर परिणाम का उपभोग करता है। LiveData के विपरीत, Flow जटिल ऑपरेटर चेन (map, filter, flatMapConcat, catch) का समर्थन करता है और पूरी तरह से थ्रेड-सेफ है। StateFlow और SharedFlow Flow के हॉट वेरिएंट हैं, जो UI स्थिति और एक-बार की घटनाओं (Snackbar, नेविगेशन) के लिए आदर्श हैं।
Channel — कोरूटीन के बीच डेटा पास करने के लिए एक और कोरूटीन अब्स्ट्रैक्शन। Channel एक कतार की तरह काम करता है: एक प्रेषक (send) और एक या अधिक प्राप्तकर्ता (receive)। बफ़र्ड चैनल (Channel(UNLIMITED), Channel(BUFFERED)) ओवरफ़्लो पर व्यवहार को कॉन्फ़िगर करने की अनुमति देते हैं। Channel का उपयोग अक्सर Flow के साथ कॉलबैक-आधारित API को कोरूटीन में ब्रिज करने के लिए किया जाता है: callbackFlow { … }।
IT Sectr में हम सभी Android परियोजनाओं में सक्रिय रूप से कोरूटीन और Flow का उपयोग करते हैं। यह एसिंक्रोनस कोड लिखने की अनुमति देता है जो सिंक्रोनस दिखता है, परीक्षण करना आसान है (runTest, TestDispatcher) और मैन्युअल थ्रेड प्रबंधन की आवश्यकता नहीं है। डेटा लोडिंग के साथ एक सरल कोरूटीन का उदाहरण:
class UserRepository(
private val api: UserApi,
private val dao: UserDao
) {
suspend fun getUsers(): List<User> = withContext(Dispatchers.IO) {
return@withContext try {
val users = api.fetchUsers()
dao.insertAll(users)
users
} catch (e: Exception) {
dao.getAll()
}
}
}
रिएक्टिव प्रोग्रामिंग — एक प्रतिमान है जहाँ डेटा एसिंक्रोनस स्ट्रीम (Observable, Publisher) के रूप में प्रसारित होता है। RxJava/RxKotlin — Android के लिए सबसे लोकप्रिय कार्यान्वयन है, जो .NET Rx से पोर्ट किया गया है। RxSwift — iOS के लिए समान लाइब्रेरी है। मुख्य घटक: Observable (ईवेंट स्रोत), Observer (सब्सक्राइबर), Scheduler (थ्रेड प्रबंधन), Operators (स्ट्रीम रूपांतरण)।
Combine — iOS 13 में पेश किया गया रिएक्टिव प्रोग्रामिंग के लिए Apple फ्रेमवर्क है। Combine Publisher (प्रकाशक) और Subscriber (सब्सक्राइबर) प्रोटोकॉल का उपयोग करता है। RxSwift के विपरीत, Combine SDK में निर्मित है और SwiftUI के साथ निकटता से एकीकृत है। Combine में ऑपरेटर: map, filter, combineLatest, zip, debounce, throttle — डेटा बाइंडिंग से लेकर UI और सर्च क्वेरी डिबाउंसिंग तक अधिकांश परिदृश्यों को कवर करते हैं।
Future और Promise — एकल एसिंक्रोनस परिणाम के साथ काम करने के लिए पैटर्न हैं। Future एक मान का प्रतिनिधित्व करता है जो बाद में उपलब्ध होगा। Promise एक मान प्रदान करने का वादा है। Rx में यह Single (एक सफल प्रतिक्रिया या त्रुटि) है, Combine में — Future Publisher। व्यवहार में, Future/Promise एकल API अनुरोधों के लिए सुविधाजनक हैं, जबकि Observable/Publisher निरंतर स्ट्रीम (जियोलोकेशन, टेक्स्ट इनपुट) के लिए हैं।
Callback और Delegate — एसिंक्रोनस संचालन के लिए क्लासिक पैटर्न हैं। Callback — एक फ़ंक्शन जो तर्क के रूप में पारित किया जाता है और संचालन पूरा होने पर कॉल किया जाता है। Delegate — एक ऑब्जेक्ट जो ईवेंट हैंडलर विधियों के साथ एक प्रोटोकॉल लागू करता है। नुकसान: "callback hell" (नेस्टेड कॉलबैक) और त्रुटि प्रबंधन जटिलता। NotificationCenter (iOS) और EventBus (Android) — प्रसारण ईवेंट तंत्र, शिथिल रूप से युग्मित संचार के लिए उपयोगी लेकिन अंतर्निहित निर्भरताएँ पैदा करते हैं।
मल्टीथ्रेडिंग उच्च प्रदर्शन का द्वार खोलता है, लेकिन साथ ही पकड़ने में मुश्किल त्रुटियों का जोखिम पैदा करता है। सबसे आम: Race Condition (दौड़ की स्थिति), Deadlock (पारस्परिक अवरोध), Livelock (सक्रिय अवरोध) और Starvation (थ्रेड भुखमरी)। इन समस्याओं को समझना किसी भी मोबाइल डेवलपर के लिए एक अनिवार्य कौशल है।
Race Condition तब उत्पन्न होती है जब दो या अधिक थ्रेड बिना सिंक्रोनाइज़ेशन के एक ही डेटा को एक साथ पढ़ते और लिखते हैं। परिणाम इस बात पर निर्भर करता है कि कौन सा थ्रेड पहले निष्पादित होता है। क्लासिक उदाहरण: दो थ्रेड एक काउंटर को बढ़ाते हैं। "पढ़ें → बढ़ाएँ → लिखें" ऑपरेशन परमाणु नहीं है, इसलिए एक साथ निष्पादित होने पर, एक वृद्धि "खो" जाती है। समाधान — परमाणु संचालन (AtomicInteger, AtomicReference) या लॉक (Mutex, Semaphore, synchronized) का उपयोग करना है।
Deadlock — एक स्थिति जहाँ प्रत्येक थ्रेड एक संसाधन रखता है और दूसरे थ्रेड द्वारा कब्जाए गए संसाधन की प्रतीक्षा करता है। कोई भी थ्रेड आगे नहीं बढ़ सकता है। घटना की शर्तें: पारस्परिक बहिष्करण, धारण और प्रतीक्षा, कोई प्रीएम्प्शन नहीं, चक्रीय प्रतीक्षा। रोकथाम: लॉक अधिग्रहण का एक एकल क्रम स्थापित करना, टाइमआउट के साथ tryLock का उपयोग करना, Lock-Free एल्गोरिदम (ConcurrentHashMap, CopyOnWriteArrayList) लागू करना।
Livelock — थ्रेड अवरुद्ध नहीं हैं लेकिन उपयोगी कार्य किए बिना लगातार एक-दूसरे को संसाधन "पास" करते रहते हैं। उदाहरण: दो लोग एक गलियारे में मिलते हैं और दोनों एक ही दिशा में चलते हुए किनारे हो जाते हैं। Starvation — एक थ्रेड को संसाधन तक पहुँच नहीं मिलती क्योंकि अन्य थ्रेड लगातार इसे रोकते हैं। समाधान: निष्पक्ष लॉक (fair locks), सावधानी के साथ थ्रेड प्राथमिकताएँ।
मल्टीथ्रेडिंग समस्याओं को रोकने के लिए सिंक्रोनाइज़ेशन प्रिमिटिव का उपयोग किया जाता है: Mutex (पारस्परिक बहिष्करण), Semaphore (एक साथ पहुँच की संख्या को सीमित करना), Lock (tryLock के साथ इंटरफ़ेस), Synchronized (JVM-स्तरीय लॉक), @MainActor (Swift — मुख्य थ्रेड पर निष्पादन की गारंटी)। Android पर Executors.newFixedThreadPool, newCachedThreadPool के माध्यम से ThreadPool भी उपलब्ध है। हालाँकि, मैन्युअल पूल प्रबंधन लीगेसी प्रोजेक्ट्स का विशेषाधिकार है; नई परियोजनाओं में कोरूटीन का उपयोग करना बेहतर है।
| उपकरण | प्लेटफ़ॉर्म | प्रकार | विशेषताएँ |
|---|---|---|---|
| DispatchQueue (GCD) | iOS | कार्य कतार | अनुक्रमिक/समानांतर, QoS प्राथमिकताएँ, थ्रेड पूल सिस्टम द्वारा प्रबंधित |
| OperationQueue | iOS | संचालन कतार | निर्भरताएँ, रद्दीकरण, maxConcurrentOperationCount |
| Coroutines + Flow | Android | कोरूटीन | हल्के, स्ट्रक्चर्ड कंकरेंसी, StateFlow, Channel |
| RxJava / RxKotlin | Android | रिएक्टिव स्ट्रीम | Observable, Schedulers, ऑपरेटरों का समृद्ध सेट |
| Combine | iOS | रिएक्टिव स्ट्रीम | Publisher/Subscriber, SwiftUI एकीकरण |
| Async/Await + Task | iOS / Android | एसिंक्रोनस मॉडल | रैखिक कोड, @MainActor, स्ट्रक्चर्ड कंकरेंसी |
अक्सर पूछे जाने वाले प्रश्न
Main Thread (UI थ्रेड) इंटरफ़ेस प्रस्तुत करने और स्पर्श प्रसंस्करण के लिए जिम्मेदार है। Background Thread पृष्ठभूमि कार्य करता है — डेटा लोडिंग, गणनाएँ, नेटवर्क कार्य। Main Thread को ब्लॉक करना इंटरफ़ेस फ़्रीज़िंग का कारण बनता है (Android पर ANR, iOS पर frozen UI)।
Race Condition — दौड़ की स्थिति जब दो थ्रेड एक साथ साझा डेटा तक पहुँचते हैं और परिणाम निष्पादन क्रम पर निर्भर करता है। सिंक्रोनाइज़ेशन के माध्यम से बचा जाता है: Mutex, Semaphore, Lock, Synchronized, @MainActor या परमाणु संचालन।
Coroutines Android के लिए आधुनिक मानक है (JetBrains, Google द्वारा समर्थित)। RxJava/RxKotlin ऑपरेटरों के समृद्ध सेट के साथ एक रिएक्टिव दृष्टिकोण है। Coroutines एसिंक्रोनस कॉल के लिए सरल हैं, RxJava जटिल डेटा स्ट्रीम के लिए अधिक शक्तिशाली है। IT Sectr में हम नई परियोजनाओं के लिए Coroutines + Flow का उपयोग करते हैं।
Deadlock — पारस्परिक अवरोध जहाँ दो थ्रेड एक-दूसरे के संसाधनों की प्रतीक्षा करते हैं। Livelock — थ्रेड अवरुद्ध नहीं हैं लेकिन उपयोगी कार्य किए बिना लगातार संसाधन पास करते हैं। दोनों समस्याएँ उचित लॉक क्रम और टाइमआउट द्वारा हल की जाती हैं।
DispatchQueue थ्रेड प्रबंधन के लिए Grand Central Dispatch (GCD) का एक अब्स्ट्रैक्शन है। Main Queue मुख्य थ्रेड पर कार्य निष्पादित करता है, Global Queues — पृष्ठभूमि थ्रेड पर। Serial Queue अनुक्रमिक निष्पादन की गारंटी देती है, Concurrent Queue — समानांतर। आधुनिक परियोजनाओं में GCD को अक्सर Async/Await और Task से बदल दिया जाता है।
सारांश
हम एक मोबाइल एप्लिकेशन टर्नकी विकसित करेंगे
IT Sectr 2017 से स्टार्टअप और व्यवसायों के लिए iOS और Android एप्लिकेशन बनाता है। हम आपको सलाह देंगे और सर्वोत्तम समाधान प्रस्तावित करेंगे।