Concorrência e Multithreading em Swift: Nível Avançado

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Concorrência e Multithreading em Swift: Nível Avançado

\n\n

Aplicativos modernos para iOS e macOS exigem alta performance e capacidade de resposta da interface. O trabalho avançado com concorrência em Swift vai muito além do uso simples do Grand Central Dispatch (GCD). Neste artigo, exploraremos as principais ferramentas do Swift 5.5+ para gerenciar tarefas assíncronas: async/await, Actors, TaskGroup e o protocolo Sendable. Você aprenderá como evitar condições de corrida, estruturar corretamente o código multithread e otimizar a performance.

\n\n

1. Fundamentos de async/await e Concorrência Estruturada

\n\n

As palavras-chave async e await são a base da concorrência moderna em Swift. Elas permitem escrever código assíncrono que se parece com código síncrono, simplificando bastante a leitura e a depuração.

\n\n

Declaração de uma função assíncrona:

\n
func fetchUserData() async throws -> User {\n    let url = URL(string: "https://api.example.com/user")!\n    let (data, _) = try await URLSession.shared.data(from: url)\n    let decoder = JSONDecoder()\n    return try decoder.decode(User.self, from: data)\n}
\n\n

Chamada de uma função assíncrona com tratamento de erros:

\n
Task {\n    do {\n        let user = try await fetchUserData()\n        print("User name: \\(user.name)")\n    } catch {\n        print("Failed to fetch user: \\(error)")\n    }\n}
\n\n

Pontos importantes:

\n
    \n
  • Concorrência Estruturada: Cada tarefa assíncrona tem um tempo de vida claro, limitado pelo escopo da Task pai.
  • \n
  • Cancelamento de tarefas: Cancelamento automático de tarefas filhas quando a Task pai é cancelada.
  • \n
  • Prioridades: Use Task(priority: .userInitiated) para tarefas importantes para o usuário.
  • \n
\n\n

2. Actors: proteção contra condições de corrida

\n\n

Antes do Swift 5.5, os desenvolvedores frequentemente usavam filas serializadas do GCD para proteger o estado compartilhado. Os Actors fornecem isolamento embutido: o acesso ao estado de um actor só é possível através de await, garantindo segurança de thread em nível de compilador.

\n\n

Exemplo de Actor para uma conta bancária:

\n
actor BankAccount {\n    private var balance: Double = 0.0\n\n    func deposit(amount: Double) {\n        balance += amount\n    }\n\n    func withdraw(amount: Double) throws {\n        guard balance >= amount else {\n            throw BankError.insufficientFunds\n        }\n        balance -= amount\n    }\n\n    func getBalance() -> Double {\n        return balance\n    }\n}\n\n// Uso\nlet account = BankAccount()\nTask {\n    await account.deposit(amount: 100.0)\n    let currentBalance = await account.getBalance()\n    print("Balance: \\(currentBalance)")\n}
\n\n

Vantagens dos Actors:

\n
    \n
  • Verificação em tempo de compilação: Tentar acessar uma propriedade de um actor sem await causará um erro de compilação.
  • \n
  • Reentrância (Reentrancy): Um actor pode pausar a execução do método atual (por exemplo, durante um await dentro do actor) e processar outra chamada, prevenindo deadlocks.
  • \n
  • Performance: Actors usam mecanismos de sincronização otimizados, geralmente mais rápidos que bloqueios manuais.
  • \n
\n\n

3. TaskGroup: processamento paralelo de dados

\n\n

Quando você precisa executar várias operações assíncronas independentes e aguardar sua conclusão, o TaskGroup é a ferramenta ideal. Ele gerencia automaticamente o número de tarefas paralelas e coleta os resultados.

\n\n

Exemplo de download paralelo de várias imagens:

\n
func downloadImages(urls: [URL])                

Blogs

Book Recommendations