Lição: 54: Atores e Concorrência Estruturada
Em Swift, os atores e a concorrência estruturada oferecem um padrão poderoso para gerenciar a concorrência e proteger estados compartilhados. Esta aula abordará os fundamentos dos atores, seu propósito e como usar a concorrência estruturada para gerenciar tarefas assíncronas de forma segura e eficiente.
O que são Atores?
Atores são um novo tipo de referência em Swift que permitem proteger estados mutáveis em um ambiente concorrente. Ao definir um ator, você garante que apenas uma tarefa acesse as propriedades e métodos do ator por vez. Isso elimina problemas relacionados a condições de corrida e sincronização.
Definindo um Ator
Aqui está um exemplo básico de um ator em Swift:
actor ContaBancaria {
private var saldo: Double = 0.0
func depositar(valor: Double) {
saldo += valor
}
func sacar(valor: Double) -> Bool {
if saldo >= valor {
saldo -= valor
return true
} else {
return false
}
}
func consultarSaldo() -> Double {
return saldo
}
}
Neste exemplo, definimos um ator ContaBancaria
que possui uma propriedade privada saldo
. Os métodos depositar
, sacar
e consultarSaldo
acessam o saldo
de forma segura dentro do contexto do ator.
Usando Atores
Quando você deseja interagir com um ator, deve fazê-lo usando chamadas assíncronas. Aqui está um exemplo de como você poderia usar o ator ContaBancaria
:
func usoExemplo() async {
let conta = ContaBancaria()
await conta.depositar(valor: 100.0)
let sucesso = await conta.sacar(valor: 50.0)
let saldoAtual = await conta.consultarSaldo()
print("Saque bem-sucedido: \(sucesso)")
print("Saldo atual: \(saldoAtual)")
}
Na função usoExemplo
, criamos uma instância de ContaBancaria
e chamamos seus métodos usando a palavra-chave await
para execução assíncrona.
Concorrência Estruturada
A concorrência estruturada nos permite gerenciar tarefas assíncronas de forma mais previsível e ordenada. Em Swift, isso é alcançado usando as palavras-chave async
e await
junto com grupos de tarefas.
Usando Grupos de Tarefas
Grupos de tarefas permitem que você execute um grupo de tarefas assíncronas de forma concorrente e reúna seus resultados. Aqui está um exemplo que demonstra grupos de tarefas com atores:
func realizarTransacoes() async {
let conta = ContaBancaria()
await withTaskGroup(of: Void.self) { grupo in
grupo.addTask {
await conta.depositar(valor: 100.0)
print("Depositou 100.0")
}
grupo.addTask {
let sucesso = await conta.sacar(valor: 50.0)
print("Saque bem-sucedido: \(sucesso)")
}
grupo.addTask {
let saldo = await conta.consultarSaldo()
print("Saldo atual: \(saldo)")
}
}
}
Na função realizarTransacoes
, criamos um grupo de tarefas e adicionamos várias tarefas a ele, cada uma interagindo com o ator ContaBancaria
. As tarefas são executadas de forma concorrente dentro do contexto estruturado do grupo de tarefas.
Tratamento de Erros com Atores
Ao lidar com código assíncrono, você também pode precisar gerenciar erros. Você pode fazer isso simplesmente propagando erros pela pilha de chamadas usando throws
.
Exemplo com Tratamento de Erros
Podemos aprimorar os métodos do nosso ator para lançar erros em caso de fundos insuficientes:
enum ErroBanco: Error {
case fundosInsuficientes
}
actor ContaBancariaAprimorada {
private var saldo: Double = 0.0
func depositar(valor: Double) {
saldo += valor
}
func sacar(valor: Double) throws {
guard saldo >= valor else {
throw ErroBanco.fundosInsuficientes
}
saldo -= valor
}
func consultarSaldo() -> Double {
return saldo
}
}
E aqui está como você pode tratar exceções:
func realizarTransacoesSeguras() async {
let conta = ContaBancariaAprimorada()
do {
await conta.depositar(valor: 200.0)
try await conta.sacar(valor: 300.0)
} catch ErroBanco.fundosInsuficientes {
print("Não há fundos suficientes para completar a transação.")
}
let saldo = await conta.consultarSaldo()
print("Saldo atual: \(saldo)")
}
Neste exemplo, o método sacar
do ator ContaBancariaAprimorada
lança um erro se não houver fundos suficientes. Tratamos o erro na função realizarTransacoesSeguras
usando um bloco do-catch
.
Conclusão
Atores e concorrência estruturada em Swift fornecem uma estrutura robusta para gerenciar estados e coordenar tarefas assíncronas. Ao usar os atores, você pode encapsular de forma segura estados mutáveis, enquanto a concorrência estruturada ajuda a manter a execução das tarefas de maneira ordenada. Com essas ferramentas, os desenvolvedores podem escrever um código concorrente mais seguro e mais fácil de manter em Swift.