.NET - Programação paralela com PLINQ - Exemplo prático

Como você já deve saber podemos alcançar o paralelismo usando as classes Parallel ou usando o Parallel LINQ e neste artigo vamos usar os recursos do Parallel LINQ para executar uma tarefa em paralelo.

Muitos computadores pessoais e estações de trabalho atualmente possuem dois ou quatro núcleos que permitem que múltiplas threads serem executadas simultaneamente. Para aproveitar esses recursos de hardware, você pode paralelizar seu código para distribuir o trabalho entre vários processadores.

No passado, a paralelização exigia uma manipulação de baixo nível das threads e bloqueios.  A partir do Visual Studio 2010 e da NET Framework 4 houve significativa melhora no suporte à programação paralela pelo fornecimento de um novo runtime, novos tipos de biblioteca de classe, e novas ferramentas de diagnóstico.

Esses recursos simplificam o desenvolvimento em paralelo de modo que você pode escrever código paralelo eficiente, refinado, e escalável em uma linguagem natural, sem ter que trabalhar diretamente com threads ou com o pool de threads. A ilustração a seguir fornece uma visão geral de alto nível da arquitetura de programação paralela na NET Framework 4.

Parallel LINQ também conhecido como PLINQ é uma implementação do processamento paralelo para LINQ to Objects.

Assim o PLINQ, disponível a partir do .NET Framework 4.0, tem a capacidade de realizar consultas usando a computação paralela. Dessa forma podemos realizar uma tarefa com PLINQ de forma que ela seja executada concorrentemente.

PLINQ implementa todos os métodos de extensão de uma consulta LINQ, tendo operadores adicionais para realizar as operações em paralelo. O grau de simultaneidade das consultas PLINQ esta baseado na capacidade do computador que executa a consulta.

Em muitos cenários (não todos) o PLINQ pode proporcionar um aumento significativo na velocidade usando todas as CPUs ou núcleos de CPU disponíveis.

Uma consulta PLINQ pode proporcionar um ganho de desempenho quanto ela realiza operações com uso intensivo da CPU.

Um problema frequente com aplicações Windows Forms ocorre quando tentamos atualizar um controle no formulário a partir de uma thread diferente da thread que criou o controle; nestes casos uma exceção InvalidOperationException é lançada com a mensagem :  “Cross-thread operation not valid: Control ‘txtLog’ accessed from a thread other than the thread it was created on.”

Neste artigo vou mostrar um exemplo prático de como usar PLINQ em uma aplicação ASP .NET.

Recursos usados

Criando o Web Site ASP .NET

Abra o  Visual Studio 2013 Express for web e clique em New Web Site;

 

A seguir selecione o template Visual Basic -> Web -> ASP .NET Empty Web Site e informe o nome Usando_Plinq e clique no botão OK;

 

A seguir clique no menu WEBSITE e em Add New Item;

 

Selecione o template Web Form e informe o nome Default.aspx e clique no botão Add;
 

 

Selecione a página Default.aspx no modo Design e clique no menu TABLE -> Insert Table;

 

Vamos criar uma tabela com 2 linhas e 2 colunas. Defina Rows = 2 e Columns = 2 e clique em OK;

 

A seguir inclua os seguintes controles na página Default.aspx a partir da ToolBox:

Defina o seguinte leiaute na página Default.aspx:

 

Vamos implementar o código em cada um dos dos eventos relacionados com os controles do formulário no arquivo Default.aspx.vb.

 

Defina o seguinte namespace na página:

 

Imports System.Net.NetworkInformation

 

1- Botão Incluir - evento Click : Permite incluir um site no ListBox a partir da caixa de texto txtUrl :

 

    Protected Sub btnIncluir_Click(sender As Object, e As EventArgs) Handles btnIncluir.Click
        If Not String.IsNullOrEmpty(txtUrl.Text) Then
            If IsUrlValida(txtUrl.Text) Then
                lbSites.Items.Add(txtUrl.Text)
                lblmsg.Text = ""
            Else
                lblmsg.Text = "Url inválida. Verifique o formato."
            End If
        End If
    End Sub

 

O código acima usa o método IsUrlValida() que verifica se uma Url informada possui um formato válido usando uma expressão Regex. A seguir vemos o código deste método:

 

Private Function IsUrlValida(url As String) As Boolean
        Return Regex.IsMatch(url, "([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?")
End Function

 

2- Botão Excluir - evento Click - Permite excluir um site da lista de sites no ListBox selecionando o site a ser excluído

 

 Protected Sub btnExcluir_Click(sender As Object, e As EventArgs) Handles btnExcluir.Click
        If lbSites.SelectedIndex <> -1 Then
            If Not String.IsNullOrEmpty(txtUrl.Text) Then
                lbSites.Items.RemoveAt(lbSites.SelectedIndex)
                lblmsg.Text = "URL excluída com sucesso"
            End If
        End If
    End Sub

 

 

3- ListBox - lbSites - evento SelectedIndexChanged - Permite selecionar um item do Listbox e exibir o seu valor na caixa de texto txtUrl:

 

Protected Sub lbSites_SelectedIndexChanged(sender As Object, e As EventArgs) Handles lbSites.SelectedIndexChanged
        txtUrl.Text = lbSites.SelectedItem.Value
End Sub

 

4- Button  - btnPingarParalelo - evento Click - Aqui criamos uma lista de sites a partir do controle ListBox e executamos a consulta PLinq exibindo o resultado no controle TextBox - txtResultado:

 

Protected Sub btnPingarParalelo_Click(sender As Object, e As EventArgs) Handles btnPingarParalelo.Click
        Dim sites As New List(Of String)
        Dim texto As New StringBuilder
        lblmsg.Text = ""
        For i = 0 To lbSites.Items.Count - 1
            sites.Add(lbSites.Items(i).Value)
        Next
        Dim respostasPing As List(Of PingReply) = TryCast((
                                  From site In sites.AsParallel().WithDegreeOfParallelism(sites.Count) _
                                 Select FazerPing(site)).ToList(), List(Of PingReply))
        For Each s In respostasPing.ToList()
            texto.Append("URL : " & Convert.ToString(s.Address) & "  Tempo : " & Convert.ToString(s.RoundtripTime) & " : " & Convert.ToString(s.Status))
            texto.AppendLine()
            txtResultado.Text = texto.ToString
        Next
    End Sub

 

A consulta LINQ parece uma consulta normal mas a diferença é que usamos o método  AsParallel() e WithDegreeOfParallelism :

Para realizar o ping em cada URL temos o método FazerPing() cujo código é visto a seguir:

    Private Function FazerPing(ByVal site As String) As PingReply
        Dim p As New Ping()
        Return p.Send(site)

    End Function

Executando o projeto teremos o resultado conforme a figura a seguir:

Pegue  o projeto completo aqui: Usando_Plinq.zip

João 4:34 Disse-lhes Jesus: A minha comida é fazer a vontade daquele que me enviou, e completar a sua obra.

   

 

Referências:


José Carlos Macoratti