I'm new to .Net 4.0's Tasks and I wasn't able to find what I thought would be a Task based replacement or implementation of a Timer, e.g. a periodic Task. Is there such a thing?
Update I came up with what I think is a solution to my needs which is to wrap the "Timer" functionality inside a Task with child Tasks all taking advantage of the CancellationToken and returns the Task to be able to participate in further Task steps.
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);
}
It depends on 4.5, but this works.
Obviously you could add a generic version that takes arguments as well. This is actually similar to other suggested approaches since under the hood Task.Delay is using a timer expiration as a task completion source.
Until now I used a LongRunning TPL task for cyclic CPU bound background work instead of the threading timer, because:
However, the TPL solution always claims a dedicated thread which is not necessary while waiting for the next action (which is most of the time). I would like to use the proposed solution of Jeff to perform CPU bound cyclic work on the background because it only needs a threadpool thread when there is work to do which is better for scalability (especially when the interval period is big).
To achieve that, I would suggest 4 adaptions:
ConfigureAwait(false)
to theTask.Delay()
to execute thedoWork
action on a thread pool thread, otherwisedoWork
will be performed on the calling thread which is not the idea of parallelismdoWork
to enable it to cancel the taskAbout point 2 I'm not sure, does async await still requires the TaskCanceledExecption or is it just best practice?
Please give your comments to the proposed solution...
Update 2016-8-30
The above solution doesn't immediately call
doWork()
but starts withawait Task.Delay().ConfigureAwait(false)
to achieve the thread switch fordoWork()
. The solution below overcomes this problem by wrapping the firstdoWork()
call in aTask.Run()
and await it.Below is the improved async\await replacement for
Threading.Timer
that performs cancelable cyclic work and is scalable (compared to the TPL solution) because it doesn’t occupy any thread while waiting for the next action.Note that in contrary with the Timer, the waiting time (
period
) is constant and not the cycle time; the cycle time is the sum of the waiting time and the duration ofdoWork()
which can vary.It's not exactly in
System.Threading.Tasks
, butObservable.Timer
(or simplerObservable.Interval
) from Reactive Extensions library is probably what you're looking for.UPDATE I am marking the answer below as the "answer" since this is old enough now that we should be using the async/await pattern. No need to downvote this anymore. LOL
As Amy answered, there is no Tasked based periodic/timer implementation. However, based upon my original UPDATE, we have evolved this into something quite useful and production tested. Thought I would share:
Output:
Simple...
I needed to trigger the recurring asynchronous tasks from a synchronous method.
This is an adaption of Jeff's answer. It is changed to take in a
Func<Task>
It also makes sure that the period is how often it is run by deducting the task's run time from the period for the next delay.