Thread Pool mobil inkişafda — əsaslar, iplik hovuzu və iş prinsipi

Müəllif: IT Sectr Dərc olunub: 2026-03-18 Oxuma vaxtı: 11 dəq

Thread Pool — bu, iplik idarəetmə mexanizmidir, burada əvvəlcədən yaradılmış iplik hovuzu tapşırıqları yerinə yetirmək üçün təkrar istifadə olunur, ipliklərin yaradılması və məhv edilməsi xərclərindən qaçınır. Mobil inkişafda iplik hovuzu fon əməliyyatları üçün istifadə olunur: şəbəkə sorğuları, şəkil emalı, verilənlər bazası işləri. Google Android Documentation (2025) məlumatına görə, ExecutorService Android-də fon ipliklərini idarə etmək üçün tövsiyə olunan üsuldur. iOS-da analoji rolu OperationQueue və qlobal eyni vaxtda (concurrent) növbələri olan GCD DispatchQueue oynayır.

Əsas məqamlar

  • Thread Pool — iplik yaratma xərcləri olmadan fon tapşırıqlarını yerinə yetirmək üçün təkrar istifadə olunan ipliklər hovuzu.
  • ExecutorService Android-də hovuzu ThreadPoolExecutor vasitəsilə konfiqurasiya edilə bilən parametrlərlə idarə edir.
  • OperationQueue iOS-da iplik hovuzunu maxConcurrentOperationCount vasitəsilə inkapsulə edir.
  • Core pool size — tapşırıqları yerinə yetirməyə hər zaman hazır olan minimum iplik sayı.
  • Work queue hovuzda boş iplik gözləyən tapşırıqları saxlayır.

Thread Pool nədir?

Thread Pool (iplik hovuzu) — bu, sabit sayda ipliklərin əvvəlcədən yaradıldığı və çoxsaylı tapşırıqları yerinə yetirmək üçün təkrar istifadə edildiyi arxitektura nümunəsidir. Hər əməliyyat üçün yeni iplik yaratmaq əvəzinə (bu baha başa gəlir: JVM-də hər iplik üçün təxminən 1 MB yığın), tapşırıqlar növbəyə yerləşdirilir və hovuzdan boş ipliklər tərəfindən yerinə yetirilir. Mobil inkişafda iplik hovuzu performans üçün kritik əhəmiyyət daşıyır — Android və iOS tətbiq üçün iplik sayını məhdudlaşdırır.

Thread Pool mobil inkişafda niyə vacibdir

İplik yaratmaq bahalı əməliyyatdır: yığın ayırma, sistemdə qeydiyyat, kontekst dəyişmə. Məhdud resurslara malik mobil cihazlarda nəzarətsiz iplik yaratma Android-də OOM (OutOfMemoryError) və iOS-da tənzimləməyə (throttling) səbəb olur. Thread Pool hər iki problemi həll edir: eyni vaxtda işləyən ipliklərin maksimum sayını məhdudlaşdırır və artıq yaradılmış iplikləri təkrar istifadə edir. Google raw Thread() əvəzinə ExecutorService tövsiyə edir, Apple Thread əvəzinə OperationQueue tövsiyə edir.

ParametrHovuzsuz (raw Thread)Thread Pool ilə
İplik yaratmaHər tapşırıq üçünHovuz yaradılarkən bir dəfə
Maksimum iplikMəhdudiyyətsiz (OOM riski)Core/max pool size ilə məhdudlaşdırılıb
İstifadəAşağı (iplik tapşırıqdan sonra ölür)Yüksək (iplik təkrar istifadə olunur)
İdarəetməƏl ilə (join, interrupt)Avtomatik (ExecutorService)
Yaddaş istehlakıHər tapşırıqla artırSabit

Thread Pool mobil inkişafda necə işləyir?

İplik hovuzu Producer-Consumer prinsipi ilə işləyir: tapşırıqlar (Runnable/Callable) bloklaşdırıcı növbəyə (BlockingQueue) yerləşdirilir. Hovuzdakı ipliklər növbədə tapşırıqları gözləyir və onları yerinə yetirmək üçün götürür. Alqoritm: boş ipliklər corePoolSize-dən azdırsa, yeni iplik yaradılır. corePoolSize-ə çatılıbsa, tapşırıq növbəyə yerləşdirilir. Növbə doludursa və ipliklər maximumPoolSize-dən azdırsa, əlavə iplik yaradılır. maximumPoolSize aşıldıqda tapşırıq RejectedExecutionHandler vasitəsilə rədd edilir.

Core Pool Size vs Maximum Pool Size

Core pool size — boş vəziyyətdə belə hovuzda saxlanılan ipliklərin sayı. Maximum pool size — növbə daşdıqda yaradıla biləcək maksimum iplik sayı. Onların arasındakı fərq müvəqqəti yaradılan və boş vaxt aşımından sonra bitən əlavə (overflow) ipliklərdir. Mobil cihazlarda iplik yaratma pik yüklərindən qaçmaq üçün corePoolSize-ün maximumPoolSize-ə bərabər qurulması tövsiyə olunur.

Work Queue və RejectedExecutionHandler

BlockingQueue icra gözləyən tapşırıqları saxlayır. Ən populyar tətbiqlər: LinkedBlockingQueue (məhdudiyyətsiz), ArrayBlockingQueue (məhdud) və SynchronousQueue (saxlamadan — tapşırıq dərhal iplikə ötürülür). Növbə və hovuz daşdıqda RejectedExecutionHandler işə düşür. Standart siyasətlər: AbortPolicy (RejectedExecutionException atır), CallerRunsPolicy (göndərənin iplikdə icra edir), DiscardPolicy və DiscardOldestPolicy.

kotlin
// Android-də Thread Pool yaratma
val threadPool = ThreadPoolExecutor(
    corePoolSize = 2,        // Minimum 2 iplik
    maximumPoolSize = 4,     // Maksimum 4 iplik
    keepAliveTime = 30L,     // Daşqın iplik ömrü
    unit = TimeUnit.SECONDS,
    workQueue = LinkedBlockingQueue<Runnable>(16),
    threadFactory = Executors.defaultThreadFactory(),
    handler = ThreadPoolExecutor.CallerRunsPolicy()
)

// Tapşırıqların göndərilməsi
threadPool.execute {
    val result = api.fetchData()
    runOnUiThread { showData(result) }
}

// Hovuzun bitirilməsi
threadPool.shutdown()
// Bütün tapşırıqların bitməsinin gözlənilməsi
threadPool.awaitTermination(10, TimeUnit.SECONDS)

Android-də Thread Pool: ExecutorService

Android java.util.concurrent vasitəsilə iplik hovuzunun bir neçə tətbiqini təqdim edir. Executors — hazır konfiqurasiyaları olan fabrik: newFixedThreadPool(n) (sabit hovuz), newCachedThreadPool() (məhdudiyyətsiz, lazım olduqda ipliklər yaradılır), newSingleThreadExecutor() (bir iplik — ardıcıl icra). Mobil layihələr üçün ağlabatan limitlə (2-4 iplik) newFixedThreadPool tövsiyə olunur, çünki cached hovuz çox sayda iplik yarada bilər.

Android-də ThreadPoolExecutor

ThreadPoolExecutor (TPE) — konfiqurasiya edilə bilən parametrləri olan ExecutorService-in tam tətbiqi. Android-də TPE AsyncTask, IntentService və JobIntentService daxilində istifadə olunur. corePoolSize, maximumPoolSize, keepAliveTime, BlockingQueue və RejectedExecutionHandler parametrləri hovuz davranışını incə tənzimləməyə imkan verir. Android üçün tövsiyələr: corePoolSize = CPU nüvələrinin sayı - 1 (IO-bound tapşırıqlar üçün) və ya nüvələrin sayı (CPU-bound tapşırıqlar üçün). Tipik tətbiqlər üçün — 2-4 iplik.

kotlin
// Executors hazır konfiqurasiyaları
// 1. 3 iplik üçün sabit hovuz
val fixedPool = Executors.newFixedThreadPool(3)

// 2. Keşlənmiş hovuz (mobil üçün tövsiyə edilmir)
val cachedPool = Executors.newCachedThreadPool()

// 3. Tək iplik (seriyalaşdırma)
val singlePool = Executors.newSingleThreadExecutor()

// 4. Planlayıcı (dövri tapşırıqlar)
val scheduler = Executors.newScheduledThreadPool(2)

// Callable və Future ilə istifadə
val future: Future<String> = fixedPool.submit(Callable {
    "Result: ${api.call()}"
})
// Nəticənin alınması (iplik bloklayır)
val result = future.get(5, TimeUnit.SECONDS)

// Hovuzun bitirilməsi
fixedPool.shutdownNow()

CoroutineDispatcher iplik hovuzu kimi

Kotlin korutinləri CoroutineDispatcher təqdim edir — iplik hovuzuna bənzər abstraksiya. Dispatchers.IO 64 iplikdən ibarət hovuzdan (məhdud) istifadə edir. Dispatchers.Default — CPU nüvələrinin sayına bərabər hovuz. CoroutineDispatcher əl ilə shutdown tələb etmir və avtomatik idarə olunur. İncə tənzimləmə üçün Executors.newFixedThreadPool(2).asCoroutineDispatcher() vasitəsilə öz ExecutorCoroutineDispatcher-inizi yaradın. Korutinlər iplik hovuzunu əvəz etmir, onu bükür.

iOS-da Thread Pool: OperationQueue və GCD

iOS iplik hovuzunu idarə etmək üçün iki əsas mexanizm təqdim edir: OperationQueue (GCD əsasında yüksək səviyyəli API) və GCD DispatchQueue (aşağı səviyyəli C-API). OperationQueue iplik hovuzunu maxConcurrentOperationCount xüsusiyyəti vasitəsilə inkapsulə edir. Standart olaraq OperationQueue system-defined maximum-dan istifadə edir (sistem yükündən asılıdır). DispatchQueue.global() sistem iplik hovuzu ilə eyni vaxtda (concurrent) növbə təqdim edir.

OperationQueue və maxConcurrentOperationCount

OperationQueue iplik hovuzunu maxConcurrentOperationCount vasitəsilə idarə edir. Qiymət 1 serial növbə yaradır (tək iplik hovuzuna bənzər). Qiymət 1-dən böyük — göstərilən limitlə eyni vaxtda hovuz. Standart olaraq maxConcurrentOperationCount = NSOperationQueueDefaultMaxConcurrentOperationCount (sistem optimumu, adətən 4-8 iplik). Operation asılılıqları, prioritetləri və ləğvi dəstəkləyir. Hər bir əməliyyat sistem hovuzundan istənilən boş iplikdə yerinə yetirilir.

swift
// 3 iplik hovuzu ilə OperationQueue
let queue = OperationQueue()
queue.maxConcurrentOperationCount = 3
queue.qualityOfService = .utility

// Əməliyyatların yaradılması
let operation1 = BlockOperation {
    let data = fetchData(from: url1)
    DispatchQueue.main.async { updateUI(data) }
}

let operation2 = BlockOperation {
    let data = fetchData(from: url2)
    DispatchQueue.main.async { updateUI(data) }
}

// Asılılıq: operation2 operation1-i gözləyir
operation2.addDependency(operation1)

// Növbəyə əlavə etmə
queue.addOperations([operation1, operation2], waitUntilFinished: false)

// Bütün əməliyyatların ləğvi
queue.cancelAllOperations()

GCD DispatchQueue iplik hovuzu kimi

DispatchQueue — Apple-dan iplik hovuzudur. Eyni vaxtda növbə (qos: .utility) cihazın cari yükünə uyğun optimallaşdırılmış sistem iplik hovuzundan istifadə edir. Müxtəlif QoS (userInteractive, userInitiated, utility, background) müxtəlif prioritetlərlə müxtəlif hovuzlara xəritələnir. DispatchGroup bir neçə tapşırığı sinxronlaşdırmağa imkan verir. DispatchWorkItem ləğv və qualityOfService-i dəstəkləyir. İncə nəzarət üçün DispatchQueue(label: qos: attributes: .concurrent) vasitəsilə öz eyni vaxtda növbələrinizi yaradın.

swift
// GCD DispatchQueue iplik hovuzu kimi
let customQueue = DispatchQueue(
    label: "com.app.background",
    qos: .utility,
    attributes: .concurrent,
    autoreleaseFrequency: .workItem
)

// Tapşırıqların hovuza göndərilməsi
customQueue.async { self.processFile(file1) }
customQueue.async { self.processFile(file2) }

// Sinxronizasiya üçün DispatchGroup
let group = DispatchGroup()
let pool = DispatchQueue.global(qos: .utility)

pool.async(group: group) { fetchData() }
pool.async(group: group) { processImage() }

group.notify(queue: .main) {
    self.showResult() // Hər iki tapşırıq bitdi
}

// Semaphore vasitəsilə eyni vaxtda işləmənin məhdudlaşdırılması
let semaphore = DispatchSemaphore(value: 3)
for url in urls {
    pool.async {
        semaphore.wait()
        download(url)
        semaphore.signal()
    }
}

Thread Pool konfiqurasiya parametrləri

İplik hovuzunun konfiqurasiyası birbaşa tətbiqin performansına təsir edir. Yanlış parametrlər CPU-nun tam istifadə edilməməsinə (çox az iplik) və ya sistemin həddindən artıq yüklənməsinə (çox çox iplik) səbəb olur. Mobil tətbiqlər üçün optimal dəyərlər məhdud resurslar və enerji istehlakı səbəbindən server dəyərlərindən fərqlənir. Əsas parametrlər: corePoolSize, maxPoolSize, queue capacity və keepAliveTime.

Optimal hovuz ölçüsünün hesablanması

IO-bound tapşırıqlar üçün düstur: corePoolSize = CPU nüvələrinin sayı × 2 (ipliklər giriş-çıxış gözləyir). CPU-bound tapşırıqlar üçün: corePoolSize = CPU nüvələrinin sayı (ipliklər daim hesablamalarla məşğuldur). Müasir mobil cihazlarda (6-8 nüvə) bu, CPU-bound üçün 6-8 iplik, IO-bound üçün 12-16 iplik verir. Praktiki testlər göstərir ki, tipik mobil tətbiq üçün 3-4 iplik optimaldır — daha çox iplik enerji istehlakını artırır, performans artımı vermir.

Queue Capacity və daşma zamanı davranış

Tapşırıq növbəsinin (work queue) ölçüsü neçə tapşırığın icra gözləyə biləcəyini müəyyən edir. Məhdudiyyətsiz növbə (limitsiz LinkedBlockingQueue) sürətli tapşırıq axınında OOM-a səbəb ola bilər. Məhdud növbə (sabit ölçülü ArrayBlockingQueue) daşdıqda tapşırıqları rədd edir. Mobil tətbiqlər üçün 16-32 tapşırıq tutumu olan ArrayBlockingQueue tövsiyə olunur. CallerRunsPolicy mobil qurğular üçün ən yaxşı RejectedExecutionHandler-dir: tapşırığı itirmək əvəzinə göndərəni yavaşladır (pressure back).

ParametrMobil üçün tövsiyəƏsaslandırma
corePoolSize2-4Mobil cihazın məhdud resursları
maxPoolSizecorePoolSize (və ya +1-2)İplik yaratma pik yüklərindən qaçınma
keepAliveTime15-30 saniyəSürətli yaddaş boşaltma, lakin tez-tez yaratma olmadan
Queue capacity16-32Buferləşdirmə və OOM riski arasında tarazlıq
HandlerCallerRunsPolicyTapşırıq itkisi olmadan geri təzyiq (backpressure)

İplik hovuzu ilə işləyərkən tipik səhvlər

Mobil tətbiq tərtibatçıları iplik hovuzundan istifadə edərkən tez-tez səhvlərə yol verirlər ki, bu da çökmələrə, yaddaş sızmalarına və qeyri-sabit işə səbəb olur. Ən çox rast gəlinənlər: ExecutorService üçün shutdown() çağırmamaq, hər əməliyyat üçün yeni hovuz yaratmaq, çox böyük hovuz, tapşırıqlar arasında deadlock, Android-də CachedThreadPool istifadə etmək.

Thread Pool-da Deadlock

Deadlock o zaman yaranır ki, hovuzdakı bir tapşırıq eyni hovuzdan başqa bir tapşırığın nəticəsini gözləyir, lakin bütün ipliklər gözləmə ilə məşğuldur. Nümunə: A tapşırığı B tapşırığını eyni hovuza göndərir və future.get() çağırır — hovuz tükənibsə, A tapşırığı B tapşırığını gözləyir, B tapşırığı isə icra oluna bilmir, çünki boş iplik yoxdur. Həll yolu: müxtəlif səviyyəli tapşırıqlar üçün ayrı hovuzlar və ya bloklaşdırıcı .get() əvəzinə async callback istifadə edin.

kotlin
// Thread Pool-da Deadlock
val pool = Executors.newFixedThreadPool(1)

// A tapşırığı B tapşırığını gözləyir — deadlock!
val futureA = pool.submit {
    // Bu tapşırıq heç vaxt yerinə yetirilməyəcək
    val futureB = pool.submit { 42 }
    futureB.get() // Əbədi bloklanır
}

// Düzəliş: ayrı hovuzlar
val workerPool = Executors.newFixedThreadPool(2)
val callbackPool = Executors.newSingleThreadExecutor()

workerPool.submit {
    callbackPool.submit {
        // Ayrı hovuzda icra olunur — deadlock mümkün deyil
    }
}

// Və ya CompletableFuture istifadə edin
workerPool.submit {
    CompletableFuture
        .supplyAsync { 42 }
        .thenAccept { result ->
            println(result)
        }
}

Bitməmiş hovuz və sızmalar

Activity-də yaradılmış ExecutorService onDestroy()-də bitirilməlidir. Əgər bu edilməzsə, ipliklər Activity məhv edildikdən sonra da yaddaşda qalır. Həll yolu: hovuzu Activity əvəzinə Application scope və ya ViewModel-də saxlayın. Korutinlər üçün viewModelScope və ya lifecycleScope istifadə edin. Hovuz Activity daxilində yaradılıbsa, onDestroy()-də mütləq pool.shutdown() çağırın. Test üçün dərhal dayandırmaq üçün es.shutdownNow() istifadə edin.

Tez-tez verilən suallar

Thread Pool adi iplikdən nə ilə fərqlənir?

Thread Pool artıq yaradılmış iplikləri çoxsaylı tapşırıqları yerinə yetirmək üçün təkrar istifadə edir. Adi iplik (raw Thread) yaradılır, bir tapşırığı yerinə yetirir və məhv edilir. İplik yaratmaq təxminən 1 MB yaddaş və ~1 ms vaxt aparır. Thread Pool xərcləri azaldır, maksimum iplik sayını məhdudlaşdırır və idarəetmə API-si (shutdown, awaitTermination) təqdim edir.

Mobil tətbiq üçün hovuzda neçə iplik olmalıdır?

Tipik mobil tətbiq üçün optimal 2-4 iplik-dir. CPU-bound tapşırıqlar üçün — CPU nüvələrinin sayı. IO-bound tapşırıqlar üçün — nüvələrin sayı × 2. Daha çox iplik enerji istehlakını və kontekst dəyişməsini artırır, performans artımı vermir. Android-də nüvələri təyin etmək üçün Process.availableProcessors() istifadə edin. iOS-da — ProcessInfo.processInfo.processorCount.

CachedThreadPool nədir və Android-də niyə təhlükəlidir?

CachedThreadPool ehtiyaca uyğun ipliklər yaradır və mövcud olanları təkrar istifadə edir. Problem: maksimum iplik sayını məhdudlaşdırmır. Əgər 100 tapşırıq eyni anda gələrsə, 100 iplik yaradılacaq. Bu, Android-də OOM-a səbəb olur (hər iplik ~1 MB). Aydın limitlə newFixedThreadPool(n) istifadə edin. CachedThreadPool yalnız kiçik həcmə zəmanət verilən qısamüddətli partlayış (burst) tapşırıqları üçün icazəlidir.

ExecutorService üçün shutdown() çağırmaq lazımdırmı?

Bəli, hovuz idarə olunan konteynerə (korutinlər kimi) aid deyilsə. shutdown() yeni tapşırıqların qəbulunu dayandırır və cari tapşırıqlar bitdikdən sonra iplikləri sonlandırır. shutdown() olmadan ipliklər yaddaşda qalır, tətbiq bitmir. Activity üçün onDestroy()-də çağırın. ViewModel üçün coroutineScope istifadə edin. Hovuzun bitirilməsi resurs idarəetməsinin məcburi hissəsidir, Cursor və ya InputStream-in bağlanmasına bənzər.

OperationQueue və DispatchQueue iplik hovuzudurmu?

Bəli, OperationQueue və DispatchQueue iOS tərəfindən təqdim edilən iplik hovuzudur. OperationQueue maxConcurrentOperationCount vasitəsilə eyni vaxtda işləməni məhdudlaşdırır. DispatchQueue.global() birbaşa nəzarət olmadan sistem iplik hovuzundan istifadə edir. Java ThreadPoolExecutor-dən fərqli olaraq, siz corePoolSize və ya queue capacity-ni idarə etmirsiniz — sistem hovuzu cari yük və cihazın enerji istehlakına uyğun avtomatik optimallaşdırır.

Xülasə

  • Thread Pool — iplik yaratma xərclərini azaldan, fon tapşırıqlarını yerinə yetirmək üçün təkrar istifadə olunan ipliklər hovuzu.
  • Android aydın hovuz ölçüsü məhdudiyyəti ilə ThreadPoolExecutor və Executors.newFixedThreadPool(n) istifadə edir.
  • iOS maxConcurrentOperationCount ilə OperationQueue və QoS hovuzları ilə GCD DispatchQueue təqdim edir.
  • Core pool size — minimum iplik sayı; maximum pool size — növbə daşdıqda maksimum.
  • Deadlock hovuzda bir tapşırığın eyni hovuzdan digər tapşırığı gözləməsi ilə yaranır.
  • CallerRunsPolicy mobil tətbiqlər üçün üstünlük təşkil edir — tapşırıq itkisi olmadan göndərəni yavaşladır.
  • Tipik mobil tətbiq üçün optimal hovuz ölçüsü shutdown() ilə bağlanan 2-4 iplikdir.

Açar təslim mobil tətbiq hazırlayacağıq

IT Sectr 2017-ci ildən startaplar və bizneslər üçün iOS və Android tətbiqləri yaradır. Sizə məsləhət verəcəyik və ən yaxşı həlli təklif edəcəyik.

Layihəni müzakirə et

Həm də oxuyun