Thread — پروسیسر وقت کی بنیادی اکائی، جس کا اپنا اسٹیک ہوتا ہے اور یہ دوسرے تھریڈز سے آزادانہ طور پر عملدرآمد کرتا ہے۔ موبائل ڈیولپمنٹ میں، تھریڈز کو کاموں کے متوازی عملدرآمد کے لیے استعمال کیا جاتا ہے تاکہ لمبی کارروائیوں کے دوران انٹرفیس ریسپانسیو رہے۔ Android java.lang.Thread، Executors اور Kotlin Coroutines کو سپورٹ کرتا ہے، iOS — Thread (Objective-C)، GCD اور OperationQueue کو۔ Android Thread دستاویزات کے مطابق، نیٹیو تھریڈ بنانے کے لیے آپریٹنگ سسٹم کو اسٹیک کے لیے تقریباً 1 MB مختص کرنے کی ضرورت ہوتی ہے۔
اہم نکات
Thread (عملدرآمد کا دھاگہ) — ہدایات کا ایک آزاد سلسلہ ہے جسے آپریٹنگ سسٹم CPU کور پر شیڈول کر سکتا ہے۔ ہر عمل (ایپلیکیشن) میں کم از کم ایک تھریڈ ہوتا ہے — Main Thread۔ کاموں کے متوازی عملدرآمد کے لیے اضافی تھریڈز بنائے جاتے ہیں۔ ہر تھریڈ کا اپنا سافٹ ویئر اسٹیک (مقامی متغیرات کے ساتھ)، پروگرام کاؤنٹر (PC) اور رجسٹر ہوتے ہیں۔ Heap میموری عمل کے تمام تھریڈز کے لیے مشترکہ ہوتی ہے۔
موبائل آپریٹنگ سسٹمز میں، تھریڈز کو preemptive multitasking کے ذریعے شیڈول کیا جاتا ہے: OS کسی بھی وقت تھریڈ کے عملدرآمد کو روک سکتا ہے اور کنٹرول دوسرے تھریڈ کو منتقل کر سکتا ہے (context switch)۔ سیاق و سباق کی تبدیلی ایک مہنگی کارروائی ہے (1-10 مائیکرو سیکنڈ)، کیونکہ اس کے لیے CPU رجسٹرز کو محفوظ/بحال کرنا، TLB کو اپ ڈیٹ کرنا اور کیشے صاف کرنا ضروری ہوتا ہے۔ اسی لیے تھریڈز کی ضرورت سے زیادہ تعداد (سینکڑوں اور ہزاروں) کارکردگی کو خراب کرتی ہے — OS عملدرآمد سے زیادہ وقت سیاق و سباق کی تبدیلی میں صرف کرتا ہے۔
تھریڈ اور عمل — مختلف تصورات ہیں۔ عمل مختص ورچوئل میموری کے ساتھ ایپلیکیشن کی ایک مثال ہے۔ عمل کے اندر تھریڈ اس میموری کو دوسرے تھریڈز کے ساتھ شیئر کرتا ہے۔ Android میں، ایپلیکیشن کا ہر جزو (Activity، Service، BroadcastReceiver) ایک عمل میں کام کرتا ہے لیکن مختلف تھریڈز پر عملدرآمد کر سکتا ہے۔ iOS ایپلیکیشن بھی ایک واحد عمل ہے جس میں GCD یا Thread کے ذریعے اضافی تھریڈز بنانے کی صلاحیت ہے۔
ہر تھریڈ Java/Kotlin (Android) اور NSThread (iOS) میں پانچ حالتوں سے گزرتا ہے: New (بنایا گیا)، Runnable (عملدرآمد کے لیے تیار)، Running (CPU پر چل رہا)، Blocked/Waiting (وسیلہ یا اطلاع کا انتظار)، Terminated (ختم ہوگیا)۔ حالتوں کے درمیان منتقلیاں OS شیڈولر اور سنکرونائزیشن پریمیٹیو کے ذریعے منظم کی جاتی ہیں۔ ڈویلپر تھریڈ کی ترجیح (Thread.setPriority()) اور اس کی حالت (sleep، join، interrupt) کو متاثر کر سکتا ہے۔
Android میں، ایک تھریڈ Blocked حالت میں اس وقت جاتا ہے جب وہ مصروف مانیٹر (synchronized) حاصل کرنے کی کوشش کرتا ہے، Object.wait() یا Thread.sleep() کال کرتا ہے۔ iOS میں — NSCondition.wait()، pthread_cond_wait() یا dispatch_semaphore_wait() کال کرنے پر۔ Blocked حالت میں، تھریڈ CPU استعمال نہیں کرتا لیکن میموری (اسٹیک) پر قبضہ کرتا ہے۔ ایک تھریڈ دوسرے تھریڈ سے InterruptedException (Java) حاصل کرکے یا isCancelled (Kotlin Coroutines) چیک کرکے روکا جا سکتا ہے (interrupted)۔
| حالت | تفصیل | منتقلی کا طریقہ |
|---|---|---|
| New | تھریڈ بنایا گیا لیکن شروع نہیں کیا گیا | Thread() کنسٹرکٹر |
| Runnable | تھریڈ عملدرآمد کے لیے تیار، CPU کا منتظر | thread.start() |
| Running | تھریڈ CPU کور پر چل رہا ہے | OS شیڈولر |
| Blocked/Waiting | تھریڈ وسیلہ، مانیٹر یا اطلاع کا منتظر | synchronized، wait()، sleep() |
| Terminated | تھریڈ نے run() مکمل کر لیا یا روک دیا گیا | run() مکمل، interrupt() |
Context switch (سیاق و سباق کی تبدیلی) — وہ کارروائی جس میں OS موجودہ تھریڈ کی حالت (رجسٹر، PC، TLB) محفوظ کرتا ہے اور دوسرے تھریڈ کی محفوظ کردہ حالت لوڈ کرتا ہے۔ موبائل سسٹمز میں (Linux + ART، iOS کے لیے XNU)، context switch 1-10 مائیکرو سیکنڈ لیتا ہے۔ اگر کوئی تھریڈ 100 مائیکرو سیکنڈ میں کام مکمل کرتا ہے اور context switch 5 لیتا ہے، تو 5% وقت ضائع ہوتا ہے۔ Context switch کو کم سے کم کرنے کے لیے iOS work stealing کے ساتھ GCD استعمال کرتا ہے، Android — fixedThreadCount کے ساتھ پولز۔
Android نچلی سطح کے java.lang.Thread سے جدید coroutines تک ترقی کر چکا ہے۔ تجرید کی ہر سطح کم اضافی اخراجات کے ساتھ زیادہ صلاحیتیں فراہم کرتی ہے۔ Thread بنیادی کلاس ہے، لیکن اسے براہ راست بنانا تجویز نہیں کیا جاتا: نیا تھریڈ پول کے زیر انتظام نہیں ہوتا، اس کی نگرانی اور منسوخی مشکل ہے۔ AsyncTask (API 30 سے متروک) ایک قدم آگے تھا لیکن میموری لیک اور کنفیگریشنز کے غیر آسان انتظام کا شکار تھا۔
HandlerThread Looper کے ساتھ Thread کی ایک خاص ذیلی کلاس ہے، جو پیغامات کی قطار پر کارروائی کر سکتی ہے۔ یہ پس منظر کے تھریڈ پر ترتیب وار کاموں کے عملدرآمد کے لیے استعمال ہوتا ہے، مثال کے طور پر Room یا فائلوں میں ڈیٹا لکھنا۔ HandlerThread start() کال کرکے بنایا جاتا ہے، اس کے بعد Handler(handlerThread.looper) کے ذریعے پیغامات اور Runnable بھیجے جا سکتے ہیں۔ handlerThread.quit() کال Looper کو روکتی ہے اور تھریڈ کو ختم کرتی ہے۔
// Android: Thread، HandlerThread اور Executors
import android.os.Handler
import android.os.HandlerThread
import java.util.concurrent.Executors
class ThreadExample {
// 1. براہ راست Thread بنانا (تجویز نہیں کیا جاتا)
fun directThread() {
val thread = Thread(Runnable {
Thread.sleep(1000)
print("Direct thread executed")
})
thread.start()
}
// 2. ترتیب وار پس منظر کے کاموں کے لیے HandlerThread
fun handlerThreadExample() {
val handlerThread = HandlerThread("BackgroundQueue")
handlerThread.start()
val handler = Handler(handlerThread.looper)
handler.post {
// پس منظر کے تھریڈ پر ترتیب وار عملدرآمد
Thread.sleep(500)
print("HandlerThread: کام مکمل ہوگیا")
}
// تھریڈ کو روکنا (جب کام ختم ہو جائیں تو عملدرآمد)
handlerThread.quitSafely()
}
// 3. Executors — تھریڈ پول
fun executorExample() {
val executor = Executors.newFixedThreadPool(4)
for (i in 1..10) {
executor.execute {
print("Task $i on thread ${Thread.currentThread().getName()}")
}
}
executor.shutdown()
}
// 4. Kotlin Coroutines — جدید معیار
suspend fun coroutineExample() = kotlinx.coroutines.withContext(
kotlinx.coroutines.Dispatchers.Default
) {
print("Coroutine on thread: ${Thread.currentThread().getName()}")
}
}
ThreadExample Android میں تھریڈز کی چاروں تجریدی سطحوں کو ظاہر کرتا ہے۔ براہ راست Thread بنانا سب سے نچلی سطح اور کم ترین موثر طریقہ ہے۔ HandlerThread پس منظر میں ترتیب وار کاموں کے لیے مفید ہے۔ Executors.newFixedThreadPool(4) 10 تک کاموں کے متوازی عملدرآمد کے لیے 4 تھریڈز کا پول بناتا ہے۔ Kotlin Coroutines Dispatchers.Default کے ساتھ — ایک جدید، موثر اور محفوظ طریقہ۔
HandlerThread — بلٹ ان Looper اور پیغام کی قطار کے ساتھ Thread کی ایک خصوصی ذیلی کلاس ہے۔ یہ start() کال کرکے بنایا جاتا ہے، اس کے بعد Handler(handlerThread.looper) کے ذریعے Runnable اور پیغامات بھیجے جا سکتے ہیں۔ HandlerThread کاموں کو سختی سے ترتیب وار انجام دیتا ہے — اگلا کام پچھلے کام کے مکمل ہونے سے پہلے شروع نہیں ہوتا۔ یہ Room یا فائلوں میں ڈیٹا لکھنے کے لیے مفید ہے، جہاں کارروائیوں کی ترتیب اہم ہوتی ہے۔ quitSafely() کال موجودہ کام مکمل ہونے کے بعد Looper کو روکتی ہے۔
iOS بھی تھریڈز کے ساتھ کام کرنے کے لیے تین سطحیں فراہم کرتا ہے۔ Thread (Swift میں Thread، Objective-C میں NSThread) — ایک نچلی سطح کا API جو براہ راست نیٹیو تھریڈ بناتا ہے۔ DispatchQueue کے ذریعے GCD (Grand Central Dispatch) — iOS ڈویلپرز کے لیے بنیادی ٹول، جو خود بخود تھریڈ پول کا انتظام کرتا ہے۔ OperationQueue — GCD کے اوپر ایک اعلیٰ سطحی تجرید جس میں انحصار، ترجیحات اور منسوخی کی معاونت ہے۔
جدید iOS ڈیولپمنٹ میں براہ راست Thread کا استعمال انتہائی نایاب ہے — GCD خودکار میموری اور تھریڈ مینجمنٹ کے ساتھ تمام ضروری صلاحیتیں فراہم کرتا ہے۔ Thread صرف مخصوص صورتوں کے لیے استعمال ہوتا ہے: thread-local storage (threadDictionary) ترتیب دینا، پس منظر کے تھریڈ کے لیے RunLoop بنانا، یا C لائبریریوں کے ساتھ انضمام جو pthread_t کی توقع کرتی ہیں۔
import Foundation
class ThreadManager {
// 1. Thread (نچلی سطح)
func createThread() {
let thread = Thread {
// کوڈ نئے تھریڈ پر عملدرآمد کرتا ہے
print("Current thread: \(Thread.current)")
}
thread.name = "com.app.worker"
thread.qualityOfService = .utility
thread.start()
}
// 2. GCD — DispatchQueue
func gcdExample() {
// متوازی قطار
let queue = DispatchQueue(label: "com.app.concurrent",
qos: .utility,
attributes: .concurrent)
queue.async {
print("GCD async task")
}
// تحریر سنکرونائزیشن کے لیے Barrier
queue.async(flags: .barrier) {
// تحریر کے دوران خصوصی رسائی
print("Barrier write: exclusive access")
}
}
// 3. انحصار کے ساتھ OperationQueue
func operationQueueExample() {
let queue = OperationQueue()
queue.maxConcurrentOperationCount = 2
queue.qualityOfService = .background
let download = BlockOperation {
print("Downloading...")
}
let process = BlockOperation {
print("Processing...")
}
let save = BlockOperation {
print("Saving...")
}
// انحصار: download -> process -> save
process.addDependency(download)
save.addDependency(process)
queue.addOperations([download, process, save], waitUntilFinished: false)
}
}
// GCD barrier کے ذریعے thread-safe مجموعہ
class ThreadSafeArray<T> {
private var array: [T] = []
private let queue = DispatchQueue(label: "com.app.concurrent",
attributes: .concurrent)
var count: Int {
return queue.sync { array.count } // concurrent read
}
func append(_ element: T) {
queue.async(flags: .barrier) { // exclusive write
self.array.append(element)
}
}
}
ThreadSafeArray کلاس GCD barrier کے ذریعے Concurrent Read / Exclusive Write پیٹرن کو ظاہر کرتی ہے۔ queue.sync{} کے ذریعے پڑھنا متعدد تھریڈز سے متوازی طور پر انجام دیا جاتا ہے۔ queue.async(flags: .barrier) کے ذریعے لکھنا تحریر مکمل ہونے تک دیگر تمام کارروائیوں (پڑھنے اور لکھنے دونوں) کو روکتا ہے۔ یہ synchronized بلاکس سے زیادہ موثر ہے کیونکہ جب تحریر نہ ہو تو قارئین کو نہیں روکتا۔
iOS میں براہ راست Thread کا استعمال تین صورتوں میں جائز ہے: thread-local storage (Thread.current.threadDictionary) — تھریڈ سے منسلک ڈیٹا ذخیرہ کرنے کے لیے؛ پس منظر کے تھریڈ پر performSelector:onThread: کے ساتھ خصوصی RunLoop بنانے کے لیے؛ C/C++ لائبریریوں کے ساتھ انضمام کے لیے جو pthread_t کی توقع کرتی ہیں۔ دیگر تمام صورتوں میں، DispatchQueue کے ذریعے GCD ترجیح رکھتا ہے — یہ خود بخود تھریڈ پول اور توانائی کی کھپت کا انتظام کرتا ہے۔
Race condition (دوڑ کی حالت) اس وقت پیدا ہوتی ہے جب دو یا زیادہ تھریڈز بیک وقت مشترکہ ڈیٹا تک رسائی حاصل کرتے ہیں، اور کم از کم ایک تھریڈ تحریر کر رہا ہو۔ نتیجہ عملدرآمد کی ترتیب (timing) پر منحصر ہوتا ہے اور غیر متوقع ہوتا ہے۔ Race condition کو روکنے کے لیے سنکرونائزیشن پریمیٹیو استعمال کیے جاتے ہیں۔ موبائل ڈیولپمنٹ میں لاکس (synchronized, NSLock)، ایٹامک آپریشنز (AtomicInteger، iOS atomic خصوصیات) اور قطاریں (serial queue) دستیاب ہیں۔
پریمیٹیو کا انتخاب منظرنامے پر منحصر ہے۔ سادہ کاؤنٹرز اور جھنڈوں کے لیے ایٹامک آپریشن کافی ہیں (AtomicInteger، atomic پراپرٹی)۔ متعدد کارروائیوں والے اہم حصوں کے لیے — لاکس (synchronized، NSLock)۔ پیچیدہ ڈیٹا ڈھانچوں کے لیے — serial DispatchQueue یا GCD barrier۔ لاکس کو سمجھنا آسان ہے لیکن وہ deadlock اور livelock کا شکار ہیں۔ قطاریں زیادہ پیچیدہ لیکن زیادہ محفوظ ہیں۔
// Android/Kotlin میں سنکرونائزیشن
import java.util.concurrent.atomic.AtomicInteger
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
class Counter {
// 1. AtomicInteger — سادہ کاؤنٹرز کے لیے
private val atomicCount = AtomicInteger(0)
fun incrementAtomic() = atomicCount.incrementAndGet()
// 2. synchronized — اہم حصوں کے لیے
@Synchronized
fun synchronizedOperation() {
// ایک وقت میں صرف ایک تھریڈ
doWork()
}
// 3. Coroutines سے Mutex — suspend-safe
private val mutex = Mutex()
suspend fun mutexOperation() {
mutex.withLock {
// protected code — thread-safe
doWork()
}
}
private fun doWork() { /* critical section */ }
}
// Deadlock مثال: A، B کو لاک کرتا ہے، B، A کو لاک کرتا ہے
class DeadlockExample {
private val lockA = Any()
private val lockB = Any()
fun methodA() = synchronized(lockA) {
Thread.sleep(100)
synchronized(lockB) { print("OK") }
}
fun methodB() = synchronized(lockB) {
Thread.sleep(100)
synchronized(lockA) { print("OK") }
}
}
Counter سنکرونائزیشن کے تین طریقوں کو ظاہر کرتا ہے۔ AtomicInteger.incrementAndGet() — بغیر لاک کے ایٹامک آپریشن (CAS)۔ @Synchronized — Java کا بلٹ ان مانیٹر، پوری آبجیکٹ کو لاک کرتا ہے۔ Mutex.withLock — coroutine mutex، تھریڈ کو روکنے کے بجائے coroutine کو معطل کرتا ہے (زیادہ موثر)۔ DeadlockExample ایک کلاسک deadlock دکھاتا ہے: دو تھریڈز مختلف ترتیب میں لاکس حاصل کرتے ہیں۔
Thread Pool (تھریڈ پول) — پہلے سے بنائے گئے تھریڈز کا ایک مجموعہ جو کاموں کو انجام دینے کے لیے دوبارہ استعمال ہوتے ہیں۔ ہر کام کے لیے نیا تھریڈ بنانے کے بجائے (مہنگا)، پول پول سے ایک خالی تھریڈ لیتا ہے۔ اگر خالی تھریڈ نہیں ہے تو، کام قطار میں لگ جاتا ہے۔ پول خود بخود سائز کا انتظام کرتا ہے: لوڈ کی چوٹیوں پر نئے تھریڈز بنائے جاتے ہیں، بیکار تھریڈز ختم کر دیے جاتے ہیں۔ یہ تھریڈ بنانے کے اضافی اخراجات کو دسیوں گنا کم کرتا ہے۔
Android میں Executors.newFixedThreadPool(4) 4 تھریڈز کا پول بناتا ہے۔ اگر ایک ساتھ 10 کام آتے ہیں، 4 فوراً شروع ہوتے ہیں، 6 قطار میں انتظار کرتے ہیں۔ Executors.newCachedThreadPool() ضرورت کے مطابق تھریڈز بناتا ہے (بغیر حد کے) اور بیکار تھریڈز کو 60 سیکنڈ کے بعد ختم کرتا ہے۔ iOS کے لیے، GCD خود بخود عالمی قطاروں کے پول فراہم کرتا ہے، جن کا سائز CPU کور کی تعداد اور موجودہ لوڈ کے مطابق ہوتا ہے۔
Kotlin Coroutines میں، تھریڈ پول dispatcher کے اندر چھپے ہوتے ہیں۔ Dispatchers.Default CPU کور کی تعداد کے برابر سائز کا پول استعمال کرتا ہے (کم از کم 2)۔ Dispatchers.IO — 64 تھریڈ (سیکڑوں IO-bound کاموں کے لیے کافی، کیونکہ زیادہ تر CPU استعمال کیے بغیر I/O کا انتظار کریں گے)۔ ہر dispatcher خود بخود پول کے سائز کو لوڈ کے مطابق ایڈجسٹ کرتا ہے، بیکار ہونے پر بیٹری کی توانائی بچاتا ہے۔
اکثر پوچھے گئے سوالات
Thread — ایپلیکیشن میں کوڈ پر عملدرآمد کی بنیادی اکائی۔ ہر عمل میں متعدد تھریڈز ہو سکتے ہیں جو میموری شیئر کرتے ہیں لیکن ان کا اپنا اسٹیک ہوتا ہے۔ موبائل ڈیولپمنٹ میں، تھریڈز UI کو روکے بغیر کاموں کو متوازی انجام دینے کے لیے استعمال ہوتے ہیں۔ Android Thread، Executors، HandlerThread اور Coroutines استعمال کرتا ہے۔ iOS Thread، GCD (DispatchQueue) اور OperationQueue استعمال کرتا ہے۔
Thread بنانا Android میں ~1 MB اور iOS میں ~512 KB اسٹیک مختص کرنے کی ضرورت ہوتی ہے — یہ ایک مہنگی کارروائی ہے۔ 1000 کاموں کے لیے، 1000 تھریڈز کا براہ راست بنانا صرف اسٹیک کے لیے تقریباً 1 GB کے علاوہ context switch کے اضافی اخراجات درکار ہوتے ہیں۔ Thread کے بجائے پولز (Executors، GCD) یا coroutines استعمال کریں — وہ تھریڈز کو دوبارہ استعمال کرتے ہیں، اضافی اخراجات کو دسیوں گنا کم کرتے ہیں۔
Race condition — متعدد تھریڈز کے بیک وقت تحریر کے ساتھ مشترکہ ڈیٹا تک رسائی پر غیر متوقع رویہ۔ تین طریقوں سے بچا جا سکتا ہے: ایٹامک اقسام (AtomicInteger) استعمال کریں، لاکس (synchronized, NSLock) استعمال کریں، یا قطار (DispatchQueue serial, Kotlin میں Actor) کے ذریعے رسائی کو ترتیب دیں۔ بہترین عمل مشترکہ mutable حالت کو کم سے کم کرنا اور immutability استعمال کرنا ہے۔
Thread — ایک نیٹیو سسٹم آبجیکٹ، جو تقریباً 1 MB اسٹیک پر قبضہ کرتا ہے اور OS کور سے منسلک ہوتا ہے۔ Coroutine — Kotlin کا ایک ہلکا پھلکا عملدرآمد یونٹ، جو کسی خاص تھریڈ سے منسلک نہیں ہوتا اور بغیر روکے معطل (suspend) ہو سکتا ہے۔ ایک تھریڈ ہزاروں coroutines انجام دے سکتا ہے۔ Coroutines میموری کے لحاظ سے زیادہ موثر ہیں اور بغیر callback کے اسینک کرونس کوڈ لکھنے کی اجازت دیتے ہیں۔
Deadlock ANR کے بغیر ایپلیکیشن کے مکمل جام ہونے کے طور پر ظاہر ہوتا ہے۔ Android میں، تمام تھریڈز کے اسٹیک ڈمپ کے لیے Thread.getAllStackTraces() استعمال کریں — دو تھریڈز ایک دوسرے کے لاکس کا انتظار کریں گے۔ iOS میں — Thread.callStackSymbols۔ ٹولز: Android Studio Profiler (Threads ٹیب)، Instruments (iOS, Thread State View)۔ روک تھام: لاکس کو مقررہ ترتیب میں حاصل کریں، timeout کے ساتھ tryLock استعمال کریں۔
خلاصہ
ہم ایک موبائل ایپلیکیشن ٹرنکی تیار کریں گے
IT Sectr 2017 سے اسٹارٹ اپس اور کاروبار کے لیے iOS اور Android ایپلیکیشنز بناتا ہے۔ ہم آپ کو مشورہ دیں گے اور بہترین حل تجویز کریں گے۔
مزید پڑھیں