Antes vamos revisar rapidamente alguns conceitos antes de discutir o método dispose assíncrono.
Por que os métodos Dispose e Finalize existem?
O .NET é um ambiente gerenciado, ou seja, ele é responsável por alocar e liberar memória e gerenciar o tempo de vida dos objetos que ele cria. Mas o que acontece quando esses objetos estão sob o domínio e controle do sistema operacional (os famosos objetos não gerenciados, como arquivos, portas, rede e outros)? É aí que entram os métodos Dispose e Finalize, que fazem uma “ponte” entre esses dois ambientes, possibilitando o gerenciamento desses recursos.
O método Dispose é usado para liberar explicitamente (por meio de código) os recursos (não gerenciados) antes que o ambiente libere o objeto.
O método Finalize, por outro lado, é chamado implícita e não deterministicamente pelo ambiente gerenciado para liberar esse recurso alocado na memória durante ou ao final da execução do aplicativo.
Ambos os métodos visam informar ao sistema operacional qual recurso não gerenciado alocado na memória precisa ser liberado. Em resumo, você controla quando e onde usar o método Dispose , enquanto não controla o método Finalize ; somente o ambiente pode chamá-lo diretamente, e ele tem um “custo” muito maior para sua aplicação em termos de desempenho e gerenciamento de memória dependendo do cenário. As melhores maneiras de usá-los e os detalhes das diferenças entre eles e outras características e comportamentos não são o foco do tópico que estamos apresentando.
Por que o método Dispose assíncrono foi criado?
Conforme explicado anteriormente, Dispose é usado para liberar explicitamente (por meio de código) os recursos (não gerenciados). Para esta execução de release, que é feita de forma síncrona , é necessário interromper o thread de execução atual para que o ambiente gerenciado “converse” com o sistema operacional e aguarde a execução das tarefas de release e retorne para que o aplicativo continue sua tarefa.
Essa jornada de execução síncrona não é vantajosa para aplicativos e linguagens mais modernos que fazem uso de vários núcleos de processador disponíveis. Com isso em mente, a possibilidade de usar a interface IAsyncDisposable foi introduzida como parte do C# 8.0 (setembro de 2019!) para permitir o descarte assíncrono de recursos não gerenciados.
Além dessa melhoria, ao longo do tempo, diversas outras melhorias foram feitas no .NET CORE para permitir operações assíncronas, adaptando o ecossistema como um todo para aproveitar eficientemente os recursos de múltiplos núcleos de processadores, o que chamamos de multithreading e processamento “paralelo”.
Principais vantagens do Dispose assíncrono
- Maior desempenho: operações assíncronas permitem que o sistema continue executando outras tarefas enquanto aguarda a conclusão do descarte, resultando em uso mais eficiente de recursos.
- Melhor escalabilidade: aplicativos que usam descarte assíncrono podem lidar com mais solicitações simultâneas, tornando-se mais escaláveis.
- Redução de bloqueios: como as operações não precisam esperar umas pelas outras, há menos bloqueios e gargalos no sistema.
Benefício em um cenário típico
Todos os serviços registrados como Scoped (ou seja, que só existem durante a solicitação HTTP atual) que implementam IAsyncDisposable serão descartados de forma assíncrona no final da solicitação, liberando tempo e recursos para processar outras solicitações reais.
O IAsyncDisposable substitui o IDisposable?
Direto ao ponto: NÃO ! A interface IAsyncDisposable não substitui a IDisposable. É uma forma adicional de descarte, principalmente para criar bibliotecas (que não controlam o código que usa o objeto a ser descartado). Se você implementar apenas IAsyncDisposable , seu aplicativo poderá ter vazamentos de recursos ou até mesmo uma exceção, dependendo do cenário .
Um ponto relevante para utilizá-lo como forma adicional é que existem algumas implementações que fazem uso de abstrações ou reflexão em busca da interface IDisposable, e como não a encontrarão, certamente teremos problemas durante a execução. Outro ponto relevante é a implementação dentro de métodos síncronos que exigem o bloqueio do contexto para sincronizá-lo, o que faz com que o uso assíncrono perca o sentido (veja a imagem abaixo).

Como implementar IAsyncDisposable junto com IDisposable

Há uma diferença no padrão de descarte assíncrono em comparação ao síncrono. Essa diferença ajuda a garantir a equivalência funcional com o padrão de descarte síncrono. Em outras palavras, o método DisposeAsyncCore() descartará recursos gerenciados de forma assíncrona, portanto, não é recomendado descartá-los de forma síncrona. Portanto, chame Dispose(false) em vez de Dispose(true) para garantir esse comportamento.
Dica: Se uma implementação for selada, o método DisposeAsyncCore() não será necessário, e a limpeza assíncrona poderá ser executada diretamente no método IAsyncDisposable.DisposeAsync().
Tenha cuidado com o empilhamento
Se uma exceção for lançada (exemplo abaixo) no construtor AnotherAsyncDisposable, nenhum objeto será descartado corretamente.

O exemplo de uma melhor construção de empilhamento é mostrado abaixo, garantindo o descarte correto para cada criação de objeto.

Algumas desvantagens da aplicação do Dispose assíncrono
- Complexidade: Implementar e gerenciar operações assíncronas pode adicionar complexidade ao código. Isso inclui a necessidade de lidar com exceções e garantir que todos os recursos sejam liberados corretamente.
- Sobrecarga de recursos: embora operações assíncronas possam melhorar a escalabilidade, elas também podem introduzir sobrecarga de recursos, como aumento de memória e uso de threads.
- Dificuldade de Depuração: Depurar código assíncrono pode ser mais complicado do que depurar código síncrono. Exceções podem ser mais difíceis de rastrear e entender, especialmente quando ocorrem em contextos de execução diferentes.
- Complexidade do teste: testar código assíncrono pode ser mais complexo, exigindo ferramentas e abordagens específicas para garantir que todas as condições e exceções sejam tratadas corretamente.
Conclusão
Muito se fala sobre o uso do Dispose assíncrono, e ele pode ser vantajoso principalmente para cenários onde o uso de recursos é caro, como em ambientes de nuvem, e desempenho, escalabilidade e poucos blocos são fatores muito desejados. No entanto, tenha cuidado ao usá-los e adicione-os somente se realmente precisar equilibrar as desvantagens.
Tenham um excelente dia! Eu sou Fernando Cerqueira e entrego estratégias digitais para os desafios do presente, com propostas de inovação para um futuro sustentável.






