Sou novo no Tasks do .Net 4.0 e não consegui encontrar o que achei que seria uma substituição baseada em tarefas ou implementação de um temporizador, por exemplo, uma tarefa periódica. Existe uma coisa dessas?
Atualização Eu vim com o que penso ser uma solução para minhas necessidades que é envolver a funcionalidade "Timer" dentro de uma Tarefa com tarefas filhas, todas aproveitando o CancelamentoToken e retornando a Tarefa para poder participar de outras etapas da Tarefa.
public static Task StartPeriodicTask(Action action, int intervalInMilliseconds, int delayInMilliseconds, CancellationToken cancelToken)
{
Action wrapperAction = () =>
{
if (cancelToken.IsCancellationRequested) { return; }
action();
};
Action mainAction = () =>
{
TaskCreationOptions attachedToParent = TaskCreationOptions.AttachedToParent;
if (cancelToken.IsCancellationRequested) { return; }
if (delayInMilliseconds > 0)
Thread.Sleep(delayInMilliseconds);
while (true)
{
if (cancelToken.IsCancellationRequested) { break; }
Task.Factory.StartNew(wrapperAction, cancelToken, attachedToParent, TaskScheduler.Current);
if (cancelToken.IsCancellationRequested || intervalInMilliseconds == Timeout.Infinite) { break; }
Thread.Sleep(intervalInMilliseconds);
}
};
return Task.Factory.StartNew(mainAction, cancelToken);
}
Respostas:
Depende do 4.5, mas funciona.
Obviamente, você pode adicionar uma versão genérica que também aceita argumentos. Na verdade, isso é semelhante a outras abordagens sugeridas, já que o Task.Delay está usando uma expiração de cronômetro como uma fonte de conclusão de tarefa.
fonte
action()
com uma repetição de!cancelToken.IsCancellationRequested
. Assim está melhor, certo?action
execução", estou certo?ATUALIZAÇÃO Estou marcando a resposta abaixo como a "resposta", pois ela já é antiga o suficiente para que possamos usar o padrão async / await. Não há necessidade de downvote isso mais. ri muito
Como Amy respondeu, não há implementação periódica / temporizada baseada em tarefas. No entanto, com base no meu UPDATE original, evoluímos para algo bastante útil e testado em produção. Pensei em compartilhar:
Resultado:
fonte
TaskScheduler.FromCurrentSynchronizationContext()
antes de configurarmainAction
. Em seguida, passo o agendador resultanteMainPeriodicTaskAction
para criar osubTask
com.Não está exatamente no
System.Threading.Tasks
, masObservable.Timer
(ou mais simplesObservable.Interval
) da biblioteca Reactive Extensions é provavelmente o que você está procurando.fonte
Até agora eu usei uma tarefa TPL LongRunning para trabalho em segundo plano cíclico de CPU em vez do temporizador de threading, porque:
No entanto, a solução TPL sempre reivindica um thread dedicado que não é necessário enquanto espera pela próxima ação (que é na maioria das vezes). Eu gostaria de usar a solução proposta de Jeff para realizar trabalho cíclico vinculado à CPU em segundo plano, porque ela só precisa de um thread de pool de threads quando há trabalho a ser feito, o que é melhor para escalabilidade (especialmente quando o período de intervalo é grande).
Para conseguir isso, gostaria de sugerir 4 adaptações:
ConfigureAwait(false)
aoTask.Delay()
para executar adoWork
ação em um thread do pool de threads, caso contráriodoWork
será realizada na thread de chamada, que não é a ideia de paralelismodoWork
para habilitá-lo a cancelar a tarefaSobre o ponto 2, não tenho certeza, o async await ainda requer a TaskCanceledExecption ou é apenas uma prática recomendada?
Dê seus comentários sobre a solução proposta ...
Atualização 2016-8-30
A solução acima não chama imediatamente,
doWork()
mas começa comawait Task.Delay().ConfigureAwait(false)
para alcançar a troca de thread paradoWork()
. A solução abaixo supera esse problema envolvendo a primeiradoWork()
chamada em aTask.Run()
e aguardando-a.Abaixo está a substituição async \ await aprimorada para
Threading.Timer
que executa trabalho cíclico cancelável e é escalonável (em comparação com a solução TPL) porque não ocupa nenhum thread enquanto espera pela próxima ação.Note que ao contrário do Timer, o tempo de espera (
period
) é constante e não o tempo do ciclo; o tempo de ciclo é a soma do tempo de espera e cuja duraçãodoWork()
pode variar.fonte
ConfigureAwait(false)
irá agendar a continuação do método para o pool de threads, então realmente não resolve o segundo ponto referente ao timer de threading. Eu também não acho quetaskState
seja necessário; A captura da variável lambda é mais flexível e segura para o tipo.await Task.Delay()
edoWork()
assimdoWork()
seria imediatamente executado durante a inicialização. Mas, sem algum truque,doWork()
ele executaria no thread de chamada pela primeira vez e o bloquearia. Stephen, você tem uma solução para esse problema?Task.Run
.Eu precisava acionar as tarefas assíncronas recorrentes de um método síncrono.
Esta é uma adaptação da resposta de Jeff. Ele é alterado para incluir um.
Func<Task>
Ele também garante que o período seja a frequência com que ela é executada, deduzindo o tempo de execução da tarefa do período para o próximo atraso.fonte
Eu tive um problema semelhante e escrevi uma
TaskTimer
classe que retorna uma série de tarefas que são concluídas no cronômetro: https://github.com/ikriv/tasktimer/ .fonte
Simples...
fonte