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.
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#
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.
Olá Leandro,
Que bom que ajudou!
Que tipo de teste vc tem feito?
Obrigado pelo comentário.
[]s!
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!
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)
Olá Rodrigo, tudo bem?
Espero que isso ajude: https://ferhenriquef.com/2014/03/22/async-methods-e-sua-comparao-com-tasks/
[]s!
Valeu Fernando, ajudou muito a esclarecer minhas dúvidas o/
Parabéns pela explicação!