DispatchQueue: cos'è, coda GCD e basi del multithreading

Autore: IT Sectr Pubblicato: 2026-03-16 Tempo di lettura: 8 min

DispatchQueue è una coda fondamentale di Grand Central Dispatch (GCD) per la gestione di attività asincrone su iOS e macOS. Secondo la Apple Developer Documentation, 2026, DispatchQueue astrae la gestione dei thread dallo sviluppatore attraverso code serial e concurrent. GCD distribuisce automaticamente le attività nel pool di thread di sistema, eliminando la necessità di creare e distruggere thread manualmente.

Punti chiave

  • DispatchQueue è l'astrazione principale di GCD per l'esecuzione di codice asincrono in iOS
  • Coda serial esegue le attività rigorosamente in sequenza, eliminando le race condition
  • Coda concurrent esegue più attività in parallelo tramite il pool di thread di sistema
  • QoS imposta la priorità dell'attività — da userInteractive a background
  • DispatchQueue.main è l'unica coda per aggiornare UIKit sul thread principale

Cos'è DispatchQueue e Grand Central Dispatch

DispatchQueue è un oggetto del framework Grand Central Dispatch (GCD) che gestisce l'esecuzione di attività in code di thread di sistema o personalizzate. Grand Central Dispatch è una libreria di basso livello di Apple, disponibile da iOS 4 e macOS 10.6, che astrae completamente la gestione dei thread dallo sviluppatore. GCD utilizza il pool di thread del sistema operativo e scala automaticamente il numero di thread in base al carico del dispositivo.

Lo sviluppatore non deve creare e distruggere thread manualmente — GCD si occupa di questo compito, fornendo una semplice API tramite DispatchQueue. Un'attività sotto forma di closure viene inviata a una coda tramite i metodi sync o async. Nel primo caso, il thread chiamante viene bloccato fino al completamento dell'attività; nel secondo, l'esecuzione continua immediatamente.

Secondo Apple (2026), GCD utilizza un pool di thread di sistema che si adatta al numero di core e al carico corrente della CPU. Una coda concurrent non crea un nuovo thread per ogni attività — GCD riutilizza i thread dal pool, minimizzando il sovraccarico della creazione di thread.

Architettura di GCD

Grand Central Dispatch è composto da tre componenti chiave: code (DispatchQueue), gruppi (DispatchGroup) e semafori (DispatchSemaphore). La coda è l'elemento principale che accetta attività sotto forma di blocchi di codice. DispatchGroup sincronizza l'esecuzione di più attività, mentre DispatchSemaphore limita l'accesso a una risorsa condivisa a un numero specifico di thread.

Ogni coda GCD è associata a una specifica classe QoS (Quality of Service), che informa il sistema sull'importanza dell'attività. Il sistema utilizza la QoS per distribuire il tempo di CPU tra le code, dando priorità alle attività più critiche — come l'aggiornamento dell'interfaccia utente o la gestione dei tocchi dell'utente.

Code serial e concurrent: confronto

Una coda serial esegue le attività rigorosamente in sequenza, una dopo l'altra. Se tre attività vengono inserite in una coda serial, la seconda inizia solo dopo il completamento completo della prima. Le code serial vengono utilizzate per sincronizzare l'accesso a risorse condivise — ad esempio, un array modificato da più parti del codice.

Una coda concurrent esegue più attività simultaneamente, distribuendole tra i thread disponibili del pool di sistema. Le attività in una coda concurrent vengono avviate in ordine FIFO, ma completate in ordine arbitrario se i loro tempi di esecuzione differiscono. Una coda concurrent non garantisce l'ordine di completamento — solo l'ordine di avvio.

ParametroCoda serialCoda concurrent
Ordine di esecuzioneRigorosamente sequenzialeParallelo
Numero di threadUnoPiù dal pool GCD
ApplicazioneProtezione risorse condiviseCalcoli indipendenti
Coda principaleSì (thread principale)No
Rischio deadlockAlto con sync sulla stessa codaBasso

Quando scegliere una coda serial

Una coda serial è ideale per attività che modificano stato condiviso — scrivere su un file, aggiornare un modello di dati o lavorare con Core Data. L'uso di una coda serial garantisce che due parti di codice non modifichino gli stessi dati contemporaneamente, eliminando le race condition senza blocchi aggiuntivi.

Quando scegliere una coda concurrent

Una coda concurrent è adatta per attività che non dipendono l'una dall'altra: caricare più immagini, richieste di rete parallele o elaborazione batch di dati. GCD decide automaticamente quante attività eseguire simultaneamente in base al numero di core della CPU e al carico attuale del sistema.

Quality of Service: priorità di esecuzione delle attività

QoS (Quality of Service) è un meccanismo di GCD che informa il sistema operativo sull'importanza e l'urgenza di un'attività. Il sistema utilizza la QoS per la pianificazione dei thread: le attività con QoS più alta ricevono più tempo di CPU e vengono avviate prima. Il valore di QoS viene passato durante la creazione di una coda o l'invio di un'attività specifica.

GCD dispone di cinque classi QoS. .userInteractive — la priorità più alta per attività relative all'interfaccia utente. .userInitiated — per attività avviate dall'utente. .utility — per attività in background che mostrano progresso. .background — per attività invisibili all'utente. .default — un livello intermedio tra userInitiated e utility, usato per impostazione predefinita.

Secondo Apple (2026), la selezione errata della QoS è una delle cause comuni di problemi di prestazioni. Eseguire un download in background con QoS .userInteractive consuma risorse dell'interfaccia utente, causando micro-ritardi nelle animazioni. Si consiglia di scegliere la QoS più bassa che offre comunque un tempo di esecuzione accettabile.

Esempio di utilizzo della QoS

Quando si carica un'immagine per la visualizzazione immediata, utilizzare .userInitiated — l'utente si aspetta il risultato. Per il precaricamento della schermata successiva, .utility è sufficiente. La sincronizzazione in background con il server viene eseguita con .background, minimizzando l'impatto sulle attività attive.

DispatchGroup e semafori: sincronizzazione delle attività

DispatchGroup permette di tracciare il completamento di un gruppo di attività. Quando tutte le attività del gruppo sono completate, GCD chiama un gestore notify sulla coda specificata. Questo è particolarmente utile quando si caricano più risorse indipendenti — dati del profilo, elenco amici e impostazioni — dove l'interfaccia deve essere aggiornata solo dopo aver ricevuto tutti i dati.

DispatchGroup supporta una chiamata sincrona wait(), che blocca il thread corrente fino al completamento di tutte le attività. Questo è comodo quando il codice non può continuare senza i risultati del gruppo. La variante asincrona notify() chiama una closure sulla coda specificata dopo il completamento di tutte le attività, senza bloccare il thread chiamante.

DispatchSemaphore per limitare il parallelismo

DispatchSemaphore controlla l'accesso a una risorsa limitando il numero di accessi concorrenti. Un semaforo con valore iniziale 3 permette al massimo tre attività parallele. La chiamata a wait() decrementa il contatore, signal() lo incrementa. Se il contatore raggiunge lo zero, il thread si blocca fino a quando una risorsa non diventa disponibile.

Esempi di codice con DispatchQueue in Swift

Vediamo tre esempi pratici di utilizzo di DispatchQueue in Swift. Il primo dimostra una chiamata async di base con ritorno al thread principale, il secondo mostra la sincronizzazione tramite una coda serial, e il terzo utilizza DispatchGroup per richieste parallele.

Chiamata async di base con ritorno a main

DispatchQueue.main è la coda serial del thread principale, destinata esclusivamente alle operazioni dell'interfaccia utente. Usatela sempre per aggiornare l'interfaccia dopo aver completato il lavoro in background.

swift
let queue = DispatchQueue.global(qos: .userInitiated)
queue.async {
    let data = self.fetchData()
    DispatchQueue.main.async {
        self.updateUI(with: data)
    }
}

Coda serial per proteggere una risorsa condivisa

Creare una coda serial personalizzata con un identificatore univoco sincronizza l'accesso a un array mutabile. Tutte le operazioni di lettura e scrittura passano attraverso un'unica coda, eliminando le race condition.

swift
let serialQueue = DispatchQueue(label: "com.app.items")
var items: [Int] = []

serialQueue.async {
    items.append(1)
}
serialQueue.async {
    let last = items.last
    DispatchQueue.main.async {
        print("Last item: \(last)")
    }
}

DispatchGroup per richieste parallele

DispatchGroup permette di lanciare più attività su una coda concurrent e ricevere una notifica quando tutte sono completate. Questo è utile quando si caricano dati per una schermata di profilo.

swift
let group = DispatchGroup()
let worker = DispatchQueue.global()

worker.async(group: group) { self.loadProfile() }
worker.async(group: group) { self.loadFriends() }
worker.async(group: group) { self.loadSettings() }

group.notify(queue: DispatchQueue.main) {
    self.showCompleteUI()
}

Errori comuni nell'uso di DispatchQueue

Il deadlock quando si chiama sync su una coda serial è l'errore più comune. Se un'attività su una coda serial chiama queue.sync sulla stessa coda, il thread si blocca per sempre. La coda attende il completamento dell'attività corrente, e l'attività attende il completamento della chiamata sync — un classico blocco reciproco.

Aggiornamento dell'interfaccia utente da un thread in background

Tutte le operazioni con UIKit devono essere eseguite sul thread principale. Xcode rileva questi errori in modalità Debug tramite il Main Thread Checker. Nelle build Release, portano a comportamenti imprevedibili: le animazioni non si avviano, l'interfaccia utente non si aggiorna e possono verificarsi crash.

Creazione eccessiva di code personalizzate

Creare centinaia di code personalizzate invece di utilizzare code globali è un antipattern. Ogni coda consuma risorse di sistema. Per la maggior parte delle attività, le code concurrent globali con diversi livelli di QoS e una o due code serial per sincronizzare i dati condivisi sono sufficienti.

Ignorare autoreleasepool nei cicli

Quando si eseguono attività di ciclo intensive in termini di risorse su una coda in background senza autoreleasepool, la memoria cresce fino al termine dell'intero ciclo. ARC rilascia gli oggetti solo all'uscita dal pool di autorelease. Avvolgete le iterazioni del ciclo in autoreleasepool { } per un rilascio tempestivo della memoria.

Domande frequenti

Qual è la differenza tra DispatchQueue e OperationQueue?

OperationQueue è costruita sopra GCD ma fornisce un'API di livello superiore con dipendenze delle operazioni, KVO e supporto per l'annullamento. DispatchQueue è una coda di basso livello per semplici attività async senza gestione delle dipendenze.

Si può fermare forzatamente un'attività in DispatchQueue?

GCD non supporta l'arresto di un'attività in esecuzione. Il metodo suspend() sospende solo le nuove attività; quella corrente viene eseguita fino alla fine. L'annullamento richiede un controllo manuale di un flag all'interno del codice dell'attività.

Quale QoS scegliere per una richiesta di rete?

Per una richiesta principale con visualizzazione immediata del risultato — .userInitiated. Per il precaricamento dei dati — .utility. Per la sincronizzazione in background — .background.

Quanti thread utilizza una coda concurrent?

GCD non fissa il numero di thread. Il pool di thread si adatta dinamicamente sotto carico, considerando i core della CPU, il carico attuale e la QoS di ogni attività. Il numero massimo è limitato dal sistema.

Perché DispatchQueue.main è obbligatorio per UIKit?

UIKit non è thread-safe — tutte le sue classi devono essere chiamate solo dal thread principale. La violazione causa comportamenti imprevedibili, aggiornamenti mancati e crash in Produzione.

Riepilogo

  • DispatchQueue è lo strumento principale di Grand Central Dispatch per attività asincrone in iOS e macOS
  • Coda serial esegue le attività in sequenza, eliminando le race condition senza blocchi
  • Coda concurrent esegue le attività in parallelo tramite il pool di thread di sistema
  • QoS determina la priorità dell'attività — da userInteractive a background
  • DispatchGroup sincronizza più attività parallele con notify sul thread principale
  • Il deadlock con sync su una coda serial occupata è un errore critico che richiede attenzione
  • Il thread principale è obbligatorio per UIKit — aggiornate l'interfaccia solo tramite DispatchQueue.main

Svilupperemo un'applicazione mobile chiavi in mano

IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.

Discuti il progetto

Leggi anche