VB .NET - Usando Tasks |
Neste artigo vou apresentar o conceito de Tasks e mostrar um exemplo básico de sua aplicação usando a linguagem VB .NET. |
A plataforma .NET versão 4.0 apresenta o novo namespace System.Threading.Tasks, o qual contém classes que permitem abstrair a funcionalidade de threading onde, na verdade, por trás dos panos, uma ThreadPool é usada.
Uma tarefa (ou task) representa uma unidade de trabalho que deverá ser realizada. Esta unidade de trabalho pode rodar em uma thread separada e é também possível iniciar uma task de forma sincronizada a qual resulta em uma espera pela thread chamada. Com tarefas, você tem uma camada de abstração mas também um bom controle sobre as threads relacionadas.
As tarefas (tasks) permitem muito mais flexibilidade na organização do trabalho que você precisa fazer. Por exemplo, você pode definir continuar o trabalho, que deve ser feito depois que uma tarefa esteja completa. Isso pode diferenciar se um tarefa foi executada com sucesso ou não.
Você também pode organizar as tarefas em hierarquia onde uma tarefa pai pode criar novas tarefas filhas que pode criar dependências e assim o cancelamento da tarefa pai também cancela suas tarefas filhas.
Mas por que eu deveria usar Tasks e não o recurso padrão de Multithreading ?
A seguir vou apresentar um exemplo que simula uma operação computacional intensa em uma aplicação Windows Forms onde iremos usar os recursos do namespaces System.Threading.Tasks.
Recursos usados :
Criando o projeto no VS Community 2015
Abra o VS Community 2015 e clique em New Project;
Selecione a linguagem Visual Basic e o template Windows Forms Application
Informe o nome VBNET_Tasks e clique no botão OK;
No formulário form1.vb inclua os seguintes controles:
Disponha os controles no formulário conforme o leiaute da figura abaixo:
Esta singela
aplicação terá o objetivo de contar números primos. Funciona assim:
A ideia é que se o usuário entrar um intervalo muito amplo de números, a operação para contar os números primos vai demorar e a interface do usuário (UI) ficará congelada até que a operação termine a menos que você, como programador que conhece Tasks, previna esse comportamento.
A abordagem tradicional onde a interface do usuário congela
Então inclua o código abaixo no formulário Form1.vb:
Imports System.Threading.Thread
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'atribui os valores máximos para o intervalo númerico
Me.nudSuperior.Maximum = Integer.MaxValue
Me.nudInferior.Maximum = Integer.MaxValue
End Sub
Private Function getPrimosNoIntervalo(valorInicialInclusivo As Integer, valorFinalExclusivo As Integer) As IEnumerable(Of Integer)
'força um atraso de 2 segundos
Sleep(TimeSpan.FromMilliseconds(2000))
'retorna quantos números primos existe no intervalo
Return getTodosNumerosPrimos(Enumerable.Range(valorInicialInclusivo, (valorFinalExclusivo - valorInicialInclusivo)))
End Function
Private Function getTodosNumerosPrimos(numeros As IEnumerable(Of Integer)) As IEnumerable(Of Integer)
'calcula a quantidade de número primos
Return From n In numeros Where (Not (n <= 1) AndAlso Enumerable.Range(2, CInt(Math.Sqrt(If(n = 2, 0, n)))).All(Function(i) n Mod i > 0)) Select n
End Function
Private Sub btnCalcular_Click(sender As Object, e As EventArgs) Handles btnCalcular.Click
'exibe a quantidade de número primos
Me.txtPrimos.Text += Me.getPrimosNoIntervalo(Integer.Parse(Me.nudInferior.Value.ToString()), Integer.Parse(Me.nudSuperior.Value.ToString())).Count().ToString() + Environment.NewLine
End Sub
Private Sub nudInferior_Validating(sender As Object, e As System.ComponentModel.CancelEventArgs) Handles nudInferior.Validating
'valida a entrada do usuário
If Me.nudInferior.Value > Me.nudSuperior.Value Then
Me.nudInferior.Value = Me.nudSuperior.Value
End If
End Sub
End Class
|
Se você executar o projeto e informar um intervalo de 0 a 200, por exemplo,e clicar no botão Calcular, vai notar que a interface do usuário vai 'congelar' e não vai permitir nenhuma nova interação do usuário enquanto estiver realizando os cálculos.
Queremos evitar esse problema permitindo que o usuário informe um intervalo de números, clique no botão Calcular e a seguir possa inserir um novo intervalo enquanto os cálculos estão sendo processados.
Vejamos então como fazer isso usando os recursos do namespace System.Threading.Tasks.
A abordagem usando Tasks
Inclua um novo Button no formulário form1.vb com o nome btnCalcularTasks conforme mostra a figura abaixo:
Para começar vamos incluir no formulário a declaração do namespace:
Imports System.Threading.Tasks
|
Agora no evento Click do botão Calcular com Tasks inclua o código abaixo:
Private Sub btnCalcularTasks_Click(sender As Object, e As EventArgs) Handles btnCalcularTasks.Click
'para evitar o acesso cross thread dos controles vamos armazenar os valores em variáveis
Dim inferior As Integer = Integer.Parse(Me.nudInferior.Value.ToString())
Dim superior As Integer = Integer.Parse(Me.nudSuperior.Value.ToString())
'inicia uma nova task, e então exibe o resultado no textbox
Task.Factory.StartNew(Of Integer)(Function() Me.getPrimosNoIntervalo(inferior, superior).Count())
End Sub
|
Vamos entender o código:
Envolvemos a chamada para o método getPrimosNoIntervalo() em uma task.
Dessa forma agora teremos uma execução assíncrona ao lado a lado com a principal Thread de interface, e com isso, a interface do usuário não vai congelar e vai continuar responsiva durante a operação de cálculo.
Neste código estamos usando uma versão genérica do método StartNew() e isso significa que queremos que a task retorne um valor (no exemplo a quantidade de números primos). Se eu não desejasse retornar um valor usaria a versão não genérica do método.
Bem, estamos realizando o cálculo mas ainda não estamos exibindo o resultado no TextBox.
É neste ponto que teríamos de usar Invoke() ou BeginInvoke() para fazer uma chamada para o segmento de interface do usuário para permitir atualizar a caixa de texto se estivéssemos usando uma thread dedicada.
Porém usando Tasks a tarefa fica mais fácil.
Primeiro vamos incluir a seguinte linha de código no início
Private scheduler As TaskScheduler = Nothing |
A seguir no construtor do formulário form1.vb inclua a linha de código abaixo:
Me.scheduler = TaskScheduler.FromCurrentSynchronizationContext()
|
Este código atribui ao campo uma tarefa agendada para a thread da interface do usuário e assim podemos usá-la para atualizar a caixa de texto.
A seguir precisamos atualizar a caixa de texto com o valor retornado da task de cálculo.
Para fazer isso usamos o método ContinueWith() que permite encadear as tarefas juntas, uma depois da outra.
Usando a classe Tasks você pode especificar que, depois que uma tarefa for concluída outra tarefa específica deve começar a ser executada, por exemplo, uma nova tarefa que usa um resultado da anterior ou que deve fazer uma limpeza se a tarefa anterior falhou.
Vamos alterar o código do evento Click do botão Calcular com Tasks conforme abaixo:
Private Sub btnCalcularTasks_Click(sender As Object, e As EventArgs) Handles btnCalcularTasks.Click
'para evitar o acesso cross thread dos controles vamos armazenar os valores em variáveis
Dim inferior As Integer = Integer.Parse(Me.nudInferior.Value.ToString())
Dim superior As Integer = Integer.Parse(Me.nudSuperior.Value.ToString())
'inicia uma nova task, e então exibe o resultado no textbox
Task.Factory.StartNew(Of Integer)(Function() Me.getPrimosNoIntervalo(inferior, superior).Count()).
ContinueWith(Function(i) Me.txtPrimos.Text = Me.txtPrimos.Text + i.Result.ToString() + Environment.NewLine, Me.scheduler)
End Sub
|
Agora podemos testar o projeto usando intervalos maiores ou menores sem que a interface do usuário fique congelada.
Pegue o projeto completo aqui : VBNET_Tasks.zip
Aquele que nem mesmo a seu próprio Filho
poupou, antes o entregou por todos nós, como nos não dará também com ele todas
as coisas?
Romanos 8:32
Veja os
Destaques e novidades do SUPER DVD Visual Basic
(sempre atualizado) : clique e confira !
Quer migrar para o VB .NET ?
Quer aprender C# ??
Quer aprender os conceitos da Programação Orientada a objetos ? Quer aprender o gerar relatórios com o ReportViewer no VS 2013 ? |
Referências:
Super DVD Vídeo Aulas - Vídeo Aula sobre VB .NET, ASP .NET e C#
Super DVD C# - Recursos de aprendizagens e vídeo aulas para C#
Curso Fundamentos da Programação Orientada a Objetos com VB .NET
Visual Basic .NET - Implementando um array de controles - Macoratti.net