Computação Paralela – Paralelismo com C#

Provavelmente você já deve ter ouvido falar sobre processamento paralelo, paralelismo ou computação paralela. Todos estes termos se referem à forma de computação na qual vários cálculos são realizados simultaneamente, resolvidos de forma separada e concorrente (em paralelo).

A computação paralela é utilizada há muitos anos, principalmente em cenários de alto desempenho que exigem processamento pesado.

final-logo-par-72dpi

Este é mais uma das maravilhas do paradigma da divisão e conquista. Este paradigma preza a divisão de um problema em duas ou mais partes, para então cada parte ser resolvida de forma simples sem interferir no funcionamento da outra para resolução do problema principal que originou sua divisão (vide http://www.ime.usp.br/~pf/analise_de_algoritmos/aulas/divide-and-conquer.html).

O Microsoft .Net Framework fornece recursos nativos que facilitam a construção de rotinas com processamento paralelo desde a versão 4.

Quero neste post falar brevemente sobre a classe Task (namespace System.Threading.Tasks – http://msdn.microsoft.com/en-us/library/system.threading.tasks.task.aspx). A classe Task prove recursos para representação de operações assíncronas, tarefa que nos auxilia na criação de procedimentos que funcionem em paralelo.

A classe Task contém algumas características particulares para sua utilização em procedimentos que devem executar paralelamente. Quando criamos uma instância de Task devemos indicar qual método deverá executar assincronamente e qual o tipo de dados retornado por este método. Usamos um método estático chamado WaitAll para iniciar a execução dos métodos assíncronos e aguardar suas respectivas execuções terminarem de executar, a fim de definir um ponto no código que aguarde o término da execução de todos os métodos assíncronos. E por fim, não menos importante, temos uma propriedade Result que armazena o conteúdo retornado pelo método assíncrono.

Para exemplificar o uso da classe Task faremos um exemplo com dois métodos executando em paralelo. Criei um método chamado GetMemoryInfo() e outro chamado GetProcessorInfo(). Estes métodos consultam dados da memória e do processador da máquina, respectivamente.

    public class ProcessorInfo {
        public string ProcessorId { get; set; }
        public string Caption { get; set; }
        public string CurrentClockSpeed { get; set; }
        public string CurrentVoltage { get; set; }
        public string Name { get; set; }
        public string NumberOfCores { get; set; }
        public string NumberOfLogicalProcessors { get; set; }
        public string ProcessorType { get; set; }
        public string Status { get; set; }
        public string SystemName { get; set; }
    }

    public class MemoryInfo {
        public string Capacity { get; set; }
        public string Caption { get; set; }
        public string Description { get; set; }
        public string InstallDate { get; set; }
        public string Speed { get; set; }
        public string Status { get; set; }
    }
        
    static IEnumerable<ProcessorInfo> GetProcessorInfo() {
        Console.WriteLine("// Leitura de processadores iniciada em: " + DateTime.Now.ToString());

        // More info about Win32_Processor class
        // http://msdn.microsoft.com/en-us/library/windows/desktop/aa394373(v=vs.85).aspx

        List<ProcessorInfo> processors = new List<ProcessorInfo>();
        ManagementClass mngmClass = new ManagementClass("Win32_Processor");
        ManagementObjectCollection mngmCollection = mngmClass.GetInstances();

        foreach (ManagementObject objMgtObj in mngmCollection)
        {
            ProcessorInfo newProcessorInfo = new ProcessorInfo();

            newProcessorInfo.Caption = objMgtObj.Properties["Caption"].Value.ToString();
            newProcessorInfo.CurrentClockSpeed = objMgtObj.Properties["CurrentClockSpeed"].Value.ToString();
            newProcessorInfo.CurrentVoltage = objMgtObj.Properties["CurrentVoltage"].Value.ToString();
            newProcessorInfo.Name = objMgtObj.Properties["Name"].Value.ToString();
            newProcessorInfo.NumberOfCores = objMgtObj.Properties["NumberOfCores"].Value.ToString();
            newProcessorInfo.NumberOfLogicalProcessors = objMgtObj.Properties["NumberOfLogicalProcessors"].Value.ToString();
            newProcessorInfo.ProcessorId = objMgtObj.Properties["ProcessorId"].Value.ToString();
            newProcessorInfo.ProcessorType = objMgtObj.Properties["ProcessorType"].Value.ToString();
            newProcessorInfo.Status = objMgtObj.Properties["Status"].Value.ToString();
            newProcessorInfo.SystemName = objMgtObj.Properties["SystemName"].Value.ToString();

            Console.WriteLine("Leitura de dados do processador {0} concluída.", newProcessorInfo.Name);

            processors.Add(newProcessorInfo);
        }

        return processors;
    }

    static IEnumerable<MemoryInfo> GetMemoryInfo() {
        Console.WriteLine("// Leitura de memórias iniciada em: " + DateTime.Now.ToString());

        // More info about Win32_PhysicalMemory class
        // http://msdn.microsoft.com/en-us/library/windows/desktop/aa394373(v=vs.85).aspx

        List<MemoryInfo> memories = new List<MemoryInfo>();
        ManagementClass mngmClass = new ManagementClass("Win32_PhysicalMemory");
        ManagementObjectCollection mngmCollection = mngmClass.GetInstances();

        foreach (ManagementObject objMgtObj in mngmCollection)
        {
            MemoryInfo newMemoryInfo = new MemoryInfo();

            newMemoryInfo.Caption = objMgtObj.Properties["Caption"].Value.ToString();
            newMemoryInfo.Status = objMgtObj.Properties["Status"].Value == null ? string.Empty : objMgtObj.Properties["Status"].Value.ToString();
            newMemoryInfo.Capacity = objMgtObj.Properties["Capacity"].Value.ToString();
            newMemoryInfo.Description = objMgtObj.Properties["Description"].Value.ToString();
            newMemoryInfo.InstallDate = objMgtObj.Properties["InstallDate"].Value == null ? string.Empty : objMgtObj.Properties["InstallDate"].Value.ToString();                
            newMemoryInfo.Speed = objMgtObj.Properties["Speed"].Value.ToString();

            Console.WriteLine("Leitura de dados da memória {0} concluída.", newMemoryInfo.Caption);

            memories.Add(newMemoryInfo);
        }

        return memories;
    }

Faremos a execução desses dois métodos paralelamente. Para isso utilizaremos os recursos providos pela classe Task. O código abaixo cria as instâncias de Task, associa os respectivos métodos que devem ser executados paralelamente, usar o método WaitAll para aguardar o término da execução dos dois métodos e os retornos dos métodos são capturados logo em seguida, por meio da propriedade Result.

static void Main(string[] args)
{
    // Criar as tarefas paralelas
    var taskProcessor = Task.Factory.StartNew<IEnumerable<ProcessorInfo>>(() => GetProcessorInfo());
    var taskMemory = Task.Factory.StartNew<IEnumerable<MemoryInfo>>(() => GetMemoryInfo());

    Console.WriteLine("* Início do processamento.");

    // Espera o término de todas elas, para garantir a integridade das informações
    Task.WaitAll(taskProcessor, taskMemory);

    // Captura os resultados
    var resultProcessor = taskProcessor.Result;
    var resultMemory = taskMemory.Result;

    Console.WriteLine("* Fim do processamento.");

    Console.Read();
}

Espero que o uso do paralelismo seja aproveitado sempre que possível. Tenho feito alguns experimentos e tenho notado alguns cenários onde o paralelismo não é uma boa saída, como por exemplo, em métodos concorrentes que façam consultas pesadas em bases de dados, o que pode causa concorrência entre suas transações. Espero em breve publicar aqui neste blog os experimentos mesclando paralelismo e acesso a dados que tenho realizado.

Dica: http://www.par.tuwien.ac.at/

Por

MSc. Fernando Henrique Inocêncio Borba Ferreira

Microsoft Most Valuable Professional – Visual C#

Publicidade

6 comentários sobre “Computação Paralela – Paralelismo com C#

  1. Muito bom o post e foi muito útil para mim. Pois tenho pesquisado recentemente sobre paralelismo e realizado testes em como obter melhor performance com o uso dele.

  2. It’s perfect time to make some plans for the future and it is time to be happy. I’ve read this post and if I could I want to suggest you few interesting things or advice. Maybe you could write next articles referring to this article. I want to read more things about it!

  3. Olá Fernando, achei muito bom o artigo, mas gostaria de saber qual a diferença entre usar o task ou criar um método async(aliás, se possível uma explicação sobre o async, pois não consegui entender direito como usar)

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

Conectando a %s

Este site utiliza o Akismet para reduzir spam. Saiba como seus dados em comentários são processados.