Se hai mai avuto problemi con le prestazioni di un'applicazione, sai che i maggiori grattacapi sono solitamente quelli relativi a attività che richiedono un'eternità, come la lettura di file di grandi dimensioni o l'invio di richieste a un server. È qui che entrano in gioco le coroutine, uno strumento straordinario che ci permette di gestire la concorrenza in modo efficiente senza che l'interfaccia utente si blocchi e l'utente si frustri.
In sostanza, stiamo parlando di un modo per scrivere codice asincrono che sembra sincrono. Invece di impantanarci in un groviglio di funzioni che si chiamano a vicenda, le coroutine ci permettono di metti in pausa e riprendi l'esecuzione di un'attività, consentendo al processore di fare altre cose mentre aspettiamo una risposta. È, in sostanza, un modo per sfruttare al meglio le risorse di sistema senza complicare inutilmente le cose.
Il mondo delle coroutine in Python
Python, essendo un linguaggio a singolo thread grazie al famoso Blocco globale dell'interprete (GIL)Ha bisogno di trucchi per evitare di rimanere bloccato. Per questo, usa il ciclo di eventiche è come un direttore d'orchestra che decide quale compito viene eseguito e quale viene lasciato in attesa in una coda di priorità. La biblioteca asincio È il pezzo chiave qui, che introduce la sintassi async y await.
Quando definiamo una funzione con async defAbbiamo creato una coroutine. Ma tieni presente che chiamarla non la fa magicamente funzionare; abbiamo bisogno aggiungilo al ciclo degli eventi, solitamente da asyncio.run()Se vogliamo eseguire più attività contemporaneamente, abbiamo opzioni come: asyncio.gather() o il più moderno asyncio.TaskGroupche consente di gestire gruppi di attività in modo strutturato, annullandole tutte se una di esse fallisce.
All'interno di questo ecosistema troviamo il oggetti attesiQuesti sono principalmente suddivisi in coroutine, task e future. Mentre i task vengono utilizzati per pianificare l'esecuzione concorrente tramite create_task(), i I futuri sono rappresentazioni di un possibile risultato che non è ancora arrivato, risultando molto utile quando si lavora con codice vecchio basato su callback.
Padroneggiare le coroutine in Kotlin e Android
Nell'ecosistema Android, le coroutine sono la salvezza per evitare il temuto "inferno delle richiamate"Questa è una situazione in cui il codice si indenta all'infinito a destra perché ogni richiesta dipende da quella precedente. Kotlin propone la seguente soluzione: funzioni di sospensione, contrassegnato dalla parola suspendche può interrompere l'esecuzione di una coroutine senza bloccare il thread in cui sono in esecuzione, impedendone così il verificarsi Errori ANR, arresti anomali e kernel panic in Android.
Affinché ciò funzioni, abbiamo bisogno di un CoroutineScopeche è l'ambito che definisce il ciclo di vita dell'attività. Abbiamo il GlobalScope Per le attività che devono rimanere attive mentre l'app è aperta, lifecycleScope collegato alla vista e alla viewModelScopeIdeale per le operazioni sui dati nel ViewModel. Se dobbiamo cambiare il thread al volo, lo usiamo withContextche è una funzione di sospensione che ci permette saltare tra diversi contesti di esecuzione.
Dispatcher: Dove viene eseguito il mio codice?
Il dispatcher decide su quale thread viene eseguita la coroutine. Non tutti i dispatcher sono uguali e scegliere quello corretto è fondamentale per evitare che l'applicazione si blocchi.
- Predefinito: Ottimizzato per attività che richiedono un elevato utilizzo della CPU, come calcoli matematici o l'elaborazione di file JSON complessi.
- QUESTO: Il miglior amico delle richieste di rete e dell'accesso al database, poiché gestisce un pool di thread progettato per attendere risposte esterne.
- Principale: Riservato esclusivamente all'interazione con l'interfaccia utente su Android.
- Senza confini: Non è collegato ad alcun thread specifico, quindi il suo comportamento è imprevedibile e va utilizzato con cautela.
Costruttori: lancio vs asincrono
Per avviare una coroutine, utilizziamo i builder. Il più comune è... launchche attiva un'attività e restituisce un Oggetto del lavoroCon questo lavoro possiamo fare un join() aspettare che finisca o un cancel() per interrompere l'operazione. D'altra parte, abbiamo asyncche è il fiore all'occhiello quando abbiamo bisogno di un risultato. async restituisce un Differitae mediante il metodo await() possiamo recuperare il valore finale.
La vera differenza è che mentre launch È ideale per attività del tipo "scatta e dimentica". async consentire eseguire più attività in parallelo e quindi sincronizzare i loro risultati, riducendo drasticamente il tempo totale di esecuzione. Ad esempio, se abbiamo due richieste che impiegano 2 secondi ciascuna, eseguirle in sequenza richiederebbe 4 secondi, ma con async Ne avrebbero presi solo due in totale.
Dalle callback alla programmazione sequenziale
Tradizionalmente, per gestire l'asincronia si utilizzavano le callback: si passava una funzione come parametro che veniva eseguita al completamento dell'attività. Sebbene questo funzioni, rende il codice... difficile da leggere e mantenereIl passaggio alle coroutine implica la trasformazione di questi metodi in funzioni di sospensione.
Su Android, questo viene spesso ottenuto utilizzando suspendCancellableCoroutine o suspendCoroutineQuesti blocchi fungono da ponte. Ci forniscono un oggetto. continuazioneQuesto ci permette di dire alla coroutine: "attendi qui finché la funzione di callback della libreria esterna non risponde, quindi riprendi l'esecuzione con questo valore". In questo modo, il ViewModel può chiamare una funzione del repository in modo lineare, racchiudendola in un blocco. try-catch per gestire gli errori di rete in modo naturale.
Le coroutine sono incredibilmente leggere rispetto ai thread tradizionali. Possiamo avviarne migliaia senza saturare la memoria, poiché non creano un thread di sistema separato per ogni istanza, ma piuttosto riutilizzare i thread esistenti tramite sospensione. Ciò consente alle applicazioni complesse di mantenere una fluidità completa, delegando il carico pesante al gestore I/O e tornando al thread principale solo per aggiornare lo schermo. Condividi questo articolo in modo che più utenti possano conoscere le informazioni