1 – Introdução
Imagine usar um aplicativo de rede social onde você precisa esperar o feed carregar completamente antes de enviar uma mensagem. Ou um sistema bancário que trava enquanto processa uma transação. Essas situações seriam frustrantes, certo?
Esses problemas são resolvidos com programação assíncrona, que nos permite executar várias tarefas ao mesmo tempo, de forma eficiente e sem bloqueios.
Mas o que é programação assíncrona?
Programação assíncrona é a execução de códigos que realizam tarefas sem bloquear o fluxo principal. Enquanto uma tarefa longa é executada, outras podem continuar sem interrupções.
Para entender melhor, precisamos falar sobre threads.
O que são threads?
Threads são trilhos de execução usados pelo sistema operacional para rodar tarefas. Em um programa comum, tudo acontece na thread principal. Se uma tarefa demorar muito nessa thread, todo o programa pode travar.-
E se usarmos várias threads?
Usar múltiplas threads permite executar tarefas paralelamente, mas também traz desafios:- Alto custo de criação.
- Consumo excessivo de recursos.
- Dificuldade de gerenciamento (deadlocks, por exemplo).
Exemplo prático
Pense em fazer café. Imagine uma pessoa fazendo todo o trabalho sozinha (síncrono) versus duas pessoas dividindo as tarefas (assíncrono). O segundo cenário é mais eficiente, e o mesmo ocorre com programação assíncrona.
2 – Problema com Tarefas Bloqueantes
O maior problema com tarefas bloqueantes é que elas travam a execução do programa até serem concluídas.
Exemplo de bloqueio na thread principal:
fun main() { //Colocar alguma cor aqui
println("Carregando dados...")
Thread.sleep(3000) // Simula uma tarefa longa que bloqueia a execução
println("Dados carregados!")
}
Saída no console:
Carregando dados...
(3 segundos de travamento)
Dados carregados!
Enquanto o programa está em estado de sleep, ele não pode executar mais nada. Sua função principal fica completamente bloqueada.
Embora múltiplas threads possam resolver o bloqueio, gerenciá-las manualmente é complexo e propenso a erros. Isso inclui:
- Deadlocks (threads esperando indefinidamente)
- Consumo excessivo de CPU e memória
- Dificuldade de sincronizar tarefas
3 - A solução moderna: As corrotinas do Kotlin
Corrotinas são abstrações modernas para programação assíncrona. Elas permitem executar tarefas concorrentes (paralelas ou não) sem bloquear a thread atual.
Como funcionam?
Pense nas corrotinas como assistentes multitarefas. Elas pegam trabalho de várias pessoas (threads) e gerenciam tudo de forma inteligente para que ninguém fique sobrecarregado.
Principais vantagens das corrotinas:
- Tarefas simultâneas: Realize várias operações, como requisições de API, sem bloqueios.
- Suspensão sem bloqueio: Você pode pausar uma corrotina (suspend) sem impedir outras tarefas de continuar.
- Gestão automática de ciclo de vida: Use escopos para associar corrotinas ao ciclo de vida de componentes, como ViewModels no Android.
- Suporte nativo no Kotlin: As corrotinas são integradas à linguagem, facilitando sua adoção.
4 – Exemplo Prático: Café Síncrono vs. Café Assíncrono
Café Síncrono:
fun main() = runBlocking {
// runBlocking: Inicia uma corrotina "bloqueante".
// Ele bloqueia a thread principal enquanto executa corrotinas dentro dele.
fazerCafeSincrono()
println("Café finalizado.")
}
suspend fun fazerCafeSincrono() {
// suspend: Permite que a função seja "suspensa" sem bloquear a thread.
// Essas funções só podem ser chamadas dentro de corrotinas ou outras funções 'suspend'.
println("Pegando a água...")
delay(1000) // delay: Suspende a execução por 1 segundo sem bloquear a thread.
println("Fervendo a água...")
delay(2000) // Simula uma tarefa longa, como ferver a água.
println("Passando o café...")
delay(1000) // Mais 1 segundo de espera, simulando o processo de passar o café.
}
Saída no console:
Pegando a água...
Fervendo a água...
Passando o café...
Café finalizado.
- Problema: Todas as tarefas dependem da anterior. O tempo total é 4 segundos.
Café Assíncrono:
import kotlinx.coroutines.*
fun main() = runBlocking {
// runBlocking: Bloqueia a thread principal enquanto executa corrotinas.
// Aqui é usado para garantir que todas as tarefas sejam concluídas antes de terminar o programa.
val agua = async { ferverAgua() }
// async: Inicia uma corrotina para executar uma tarefa e retorna um "Deferred".
// O "Deferred" é como uma promessa de que a tarefa retornará um resultado no futuro.
val filtro = async { prepararFiltro() }
// Outra corrotina é iniciada para preparar o filtro, em paralelo com 'ferverAgua'.
agua.await()
// await: Suspende a execução até que o resultado da corrotina associada (agua) esteja pronto.
filtro.await()
// Aguarda a conclusão da corrotina que prepara o filtro.
println("Passando o café...")
delay(1000) // delay: Suspende a execução por 1 segundo sem bloquear a thread.
println("Café finalizado.")
}
suspend fun ferverAgua() {
// suspend: Permite que essa função seja usada em corrotinas.
println("Pegando a água...")
delay(1000) // Suspende a execução por 1 segundo.
println("Fervendo a água...")
delay(2000) // Simula o tempo necessário para ferver a água.
}
suspend fun prepararFiltro() {
println("Preparando o filtro e o pó...")
delay(1500) // Simula o tempo necessário para preparar o filtro.
}
Saída no console:
Pegando a água...
Preparando o filtro e o pó...
Fervendo a água...
Passando o café...
Café finalizado.
- Benefício: As tarefas são feitas paralelamente. O tempo total é reduzido para 3 segundos.
5 – Conclusão
Como vimos ao longo deste artigo, a programação assíncrona é uma abordagem essencial para resolver problemas de bloqueio e aproveitar melhor os recursos.
Principais aprendizados:
- As threads têm limitações importantes, mas as corrotinas do Kotlin oferecem uma solução eficiente e moderna.
- Usar corrotinas permite escrever código mais simples, legível e escalável.
No próximo artigo, exploraremos como usar Dispatchers e Contextos para gerenciar threads e otimizar a execução de tarefas com corrotinas.
Referência:
Documentação oficial do Kotlin sobre corrotinas
Top comments (0)