.NET Core - Apresentando a classe Channel<T>
Neste artigo vou apresentar a classe Channel<T> que foi introduzida com o .NET Core 3.X. |
Na documentação oficial a definição da classe Channel<T>, que pertence ao namespace System.Threading.Channels, diz que a classe Channel<T> fornece uma classe raiz para canais que dão suporte à leitura e gravação de elementos do tipo T.
Nem preciso dizer que esta definição não ajuda a entender o que é e nem para que serve um channel.
Então vamos tentar destrinchar esse conceito...
Em sua essência, um Channel ou Canal é essencialmente um novo tipo de coleção na plataforma .NET que atua de forma muito semelhante ao tipo Queue<T> existente mas com benefícios adicionais.
Assim um channel ou canal pode ser entendido como uma estrutura de dados básica para estabelecer uma conexão entre produtores e consumidores.
Podemos pensar em um canal como sendo um conceito de sincronização que
suporta a passagem de dados entre produtores e consumidores, normalmente
simultaneamente. Um ou mais produtores podem gravar dados no canal, que são
então lidos por um ou mais consumidores.
Logicamente, um canal é efetivamente uma fila
thread-safe eficiente.
Dessa forma este namespace contém tipos que podemos usar para implementar um cenário de comunicação produtor-consumidor permitindo que os atores realizem tarefas simultâneas, e, usando tipos de sincronização, possam trocar dados assíncronos entre si.
Temos
assim os típicos atores que atuam em uma fila : produtores
ou publishers e consumidores ou
subscribers, onde, existe alguém publicando uma
mensagem e um ou vários 'assinantes' ouvindo essa mensagem e agindo de
acordo a ela. Não há distribuição de carga porque, conforme você adiciona
assinantes, eles basicamente obtêm uma cópia das mesmas mensagens que todos os
outros.
Para tentar facilitar o entendimento temos a seguir um diagrama simplificando
descrevendo o processo:
E ainda para ilustrar o processo produtos/consumidor para mostrar como usar channel e em como este recurso pode ser útil é pensar em na seguinte situação:
Os clientes em um mercado/shopping estão se dirigindo aos caixas;
A medida que a quantidade de clientes aumente as filas nos caixas ficam mais longas;
Para processar os clientes basta abrir mais caixas de atendimento;
Se não forem abertos mais caixas para atendimento as filas vão crescer;
Se você abrir caixas para atendimento em excesso , não haverá clientes, e eles ficaram ociosos;
Geralmente, isso é chamado de problema produtor-consumidor e que o Channels visa corrigir.
Channel : Exemplo básico
Agora vejamos um exemplo extremamente básico de código usando Channel.
Os recursos necessários que precisamos usar estão contidos no namespace System.Threading.Channels onde temos os métodos para criar, usar e fechar um canal.
Vamos criar um projeto Console no ambiente do .NET 5.0 e criar um canal bem simples:
Obs: Aqui estou usando o recurso Top Level Statement do C# 9.
Entendendo o código:
1- Criamos um canal usando o método CreateUnbounded<T>() que cria um canal sem capacidade definida que esta limitado a memória RAM disponível. Criar canais dessa forma pode ser perigoso uma vez que eles não tem limitação e podem consumir toda a memória disponível.
2- A seguir estamos escrevendo 10 itens do tipo int no canal usando o método WriteAsync();
3- A seguir estamos percorrendo o canal e lendo e exibindo os itens escritos usando o método ReadAsync();
Cabe destacar que os Channels são Thread-Safe o que permite que vários threads podem estar lendo/gravando no mesmo canal sem problemas.
A seguir temos os principais métodos presentes neste namespace:
CreateBounded<T>(int capacity)
Cria um canal com capacidade de mensagens definida. Quando o canal atingir a
capacidade máxima ele só vai poder
receber uma nova mensagem depois que uma mensagem for consumida;
Channel<T> CreateBounded<T>(BoundedChannelOptions
options)
Atua da mesma forma que o anterior permitindo possibilidades de definições
de opções para denotar o comportamento do canal durante uma operação de
gravação quando o canal esta cheio e que são:
Wait - espera que o canal tenha espaço para completar uma operação escrita;
DropNewest - descarta o item mais novo escrito mas ainda não lido;
DropOldest - descarta o item mais antigo mas ainda não lido;
DropWrite
- descarta um item que esteja sendo escrito no canal;
Channel<T> CreateUnbounded<T>()
Cria um canal com capacidade ilimitada (memória RAM)
Channel<T> CreateUnbounded<T>(UnboundedChannelOptions
options)
Atua da mesma forma que o anterior permitindo possibilidades de definições
de opções no canal que são:
Essas classes herdam da classe ChannelOptions que fornece opções para controlar o comportamento do canal e que possui as propriedades:
Agora com o uso do recurso Channels você pode separar os produtores dos consumidores em um cenário de publicação e assinatura, onde os produtores e consumidores não apenas melhoram o desempenho trabalhando em paralelo, mas também é possível criar mais produtores ou consumidores caso uma dessas tarefas comece a superar a outra aumentando assim o rendimento da sua aplicação.
Em outro artigo vamos ver como podemos usar este recurso de uma forma mais útil.
"Quão grandes são, Senhor, as tuas obras! Mui profundos são os teus
pensamentos."
Salmos 92:5
Referências:
ASP .NET - Gerando QRCode com a API do Google
ASP .NET Core - Gerenciador de Despesas ...
ASP .NET Core - Acessando dados com Entity .
ASP.NET Core Web API - Tratamento de erros
ASP .NET Core MVC - Tratamento de exceções - II
ASP .NET Core - CRUD usando Blazor e Entity ..
ASP .NET Core Blazor