ہر موبائل ایپلی کیشن ایک ساتھ متعدد کام انجام دیتی ہے: نیٹ ورک سے ڈیٹا لوڈ کرنا، صارف کے ٹچ پر کارروائی کرنا، انٹرفیس کو متحرک کرنا اور فائلیں محفوظ کرنا۔ اگر یہ تمام کوڈ ایک ہی تھریڈ میں چلتا ہے تو کسی بھی نیٹ ورک تاخیر پر ایپلی کیشن منجمد ہو جاتی ہے۔ ملٹی تھریڈنگ اور کنکرنسی کلیدی تصورات ہیں جو ایپلی کیشن کو رد عمل اور موثر رہنے دیتے ہیں۔ اس مضمون میں ہم تمام اہم ٹولز کا جائزہ لیں گے: 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 ایپلیکیشنز بناتا ہے۔ ہم آپ کو مشورہ دیں گے اور بہترین حل تجویز کریں گے۔