Como processar itens na fila em série em um fio de baixa prioridade usando extensões paralelas
-
27-09-2019 - |
Pergunta
Quero saber qual a melhor maneira de processar os resultados de um processo de longa duração em série, mas em um tópico de baixa prioridade usando o .NET 4.0 Extensões paralelas.
Eu tenho a aula a seguir que faz a execução e fornece os resultados:
public class Step
{
public string Name { get; set; }
public TimeSpan Duration { get; set; }
public bool Completed { get; set; }
public void Execute()
{
DateTime before = DateTime.Now;
RemotedService.DoSomeStuff();
Duration = DateTime.Now - before;
Completed = true;
}
}
Como posso processar essas etapas e também salvá -las em um arquivo, para que sejam processadas? Eu gostaria de salvá -los em um arquivo enquanto RemotedService.DoSomeStuff()
está aguardando uma resposta do servidor.
Escrever para o arquivo seria assim:
using (StreamWriter w = File.AppendText("myFile.txt"))
{
var completedStep = completedSteps.Dequeue();
w.WriteLine(string.Format("Completed {0} with result: {1} and duration {2}",
completedStep.Name,
completedStep.Completed,
completedStep.Duration));
}
Uma opção que vem à mente é adicioná -los a um Queue
e ter um Timer
Isso os processa. Mas isso não aproveita o tempo de inatividade das chamadas remotas.
Outra opção que vem à mente é escrever assíncronos cada resultado no arquivo usando um System.Threading.Tasks.Task
por Step
, mas isso não garante que eles sejam salvos em ordem e também podem introduzir a contenção com a gravação do arquivo.
Solução
Eu sugeriria criar um BlockingCollection<Step>
(Vejo System.Collections.Concurrent
espaço para nome). À medida que cada etapa é concluída, é adicionada a essa coleção. O comportamento padrão de BlockingCollection
é funcionar como uma fila, para que você obtenha o comportamento do FIFO que está procurando.
Um segundo thread atende à fila, removendo cada item e gravando -o para um arquivo de log.
Então, se você adicionou a fila:
static private BlockingCollection<Step> LogQueue = new BlockingCollection<Step>();
Você adicionaria isso ao seu Execute
Método, após a conclusão do item:
LogQueue.Add(this);
E seu tópico de registro, que você iniciaria no construtor estático:
static void LoggingThread()
{
using (var w = new StreamWriter(...))
{
while (!LogQueue.IsCompleted())
{
Step item;
if (LogQueue.TryTake(out item))
{
w.WriteLine(....);
}
}
}
}
O tópico de registro como eu escrevi, usa um System.Threading
fio. Pode haver uma maneira mais fácil ou melhor de fazê -lo com o TPL. Ainda não estou muito familiarizado com a TPL, então não poderia dizer.
Outras dicas
Uma abordagem é criar um agendador de tarefas personalizado (ver http://msdn.microsoft.com/en-us/library/ee789351.aspx). Seu agendador de tarefas personalizado pode limitar a simultaneidade a uma execução estrita em ordem.
Criar um agendador de tarefas também permite controlar a prioridade do thread, ou o ApartmentState, que pode ser desejável em algumas situações.
Você está literalmente descrevendo o caso de uso da Workflow Foundation - ele faz tudo isso para você :)