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
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:
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication7
{
class Program
{
static void Main(string[] args)
{
Task perdiodicTask = PeriodicTaskFactory.Start(() =>
{
Console.WriteLine(DateTime.Now);
}, intervalInMilliseconds: 2000, // fire every two seconds...
maxIterations: 10); // for a total of 10 iterations...
perdiodicTask.ContinueWith(_ =>
{
Console.WriteLine("Finished!");
}).Wait();
}
}
///
/// Factory class to create a periodic Task to simulate a using Tasks.
///
public static class PeriodicTaskFactory
{
///
/// Starts the periodic task.
///
/// The action.
/// The interval in milliseconds.
/// The delay in milliseconds, i.e. how long it waits to kick off the timer.
/// The duration.
/// If the duration is set to 10 seconds, the maximum time this task is allowed to run is 10 seconds.
/// The max iterations.
/// if set to true executes each period in a blocking fashion and each periodic execution of the task
/// is included in the total duration of the Task.
/// The cancel token.
/// used to create the task for executing the .
/// A
///
/// Exceptions that occur in the need to be handled in the action itself. These exceptions will not be
/// bubbled up to the periodic task.
///
public static Task Start(Action action,
int intervalInMilliseconds = Timeout.Infinite,
int delayInMilliseconds = 0,
int duration = Timeout.Infinite,
int maxIterations = -1,
bool synchronous = false,
CancellationToken cancelToken = new CancellationToken(),
TaskCreationOptions periodicTaskCreationOptions = TaskCreationOptions.None)
{
Stopwatch stopWatch = new Stopwatch();
Action wrapperAction = () =>
{
CheckIfCancelled(cancelToken);
action();
};
Action mainAction = () =>
{
MainPeriodicTaskAction(intervalInMilliseconds, delayInMilliseconds, duration, maxIterations, cancelToken, stopWatch, synchronous, wrapperAction, periodicTaskCreationOptions);
};
return Task.Factory.StartNew(mainAction, cancelToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
}
///
/// Mains the periodic task action.
///
/// The interval in milliseconds.
/// The delay in milliseconds.
/// The duration.
/// The max iterations.
/// The cancel token.
/// The stop watch.
/// if set to true executes each period in a blocking fashion and each periodic execution of the task
/// is included in the total duration of the Task.
/// The wrapper action.
/// used to create a sub task for executing the .
private static void MainPeriodicTaskAction(int intervalInMilliseconds,
int delayInMilliseconds,
int duration,
int maxIterations,
CancellationToken cancelToken,
Stopwatch stopWatch,
bool synchronous,
Action wrapperAction,
TaskCreationOptions periodicTaskCreationOptions)
{
TaskCreationOptions subTaskCreationOptions = TaskCreationOptions.AttachedToParent | periodicTaskCreationOptions;
CheckIfCancelled(cancelToken);
if (delayInMilliseconds > 0)
{
Thread.Sleep(delayInMilliseconds);
}
if (maxIterations == 0) { return; }
int iteration = 0;
////////////////////////////////////////////////////////////////////////////
// using a ManualResetEventSlim as it is more efficient in small intervals.
// In the case where longer intervals are used, it will automatically use
// a standard WaitHandle....
// see http://msdn.microsoft.com/en-us/library/vstudio/5hbefs30(v=vs.100).aspx
using (ManualResetEventSlim periodResetEvent = new ManualResetEventSlim(false))
{
////////////////////////////////////////////////////////////
// Main periodic logic. Basically loop through this block
// executing the action
while (true)
{
CheckIfCancelled(cancelToken);
Task subTask = Task.Factory.StartNew(wrapperAction, cancelToken, subTaskCreationOptions, TaskScheduler.Current);
if (synchronous)
{
stopWatch.Start();
try
{
subTask.Wait(cancelToken);
}
catch { /* do not let an errant subtask to kill the periodic task...*/ }
stopWatch.Stop();
}
// use the same Timeout setting as the System.Threading.Timer, infinite timeout will execute only one iteration.
if (intervalInMilliseconds == Timeout.Infinite) { break; }
iteration++;
if (maxIterations > 0 && iteration >= maxIterations) { break; }
try
{
stopWatch.Start();
periodResetEvent.Wait(intervalInMilliseconds, cancelToken);
stopWatch.Stop();
}
finally
{
periodResetEvent.Reset();
}
CheckIfCancelled(cancelToken);
if (duration > 0 && stopWatch.ElapsedMilliseconds >= duration) { break; }
}
}
}
///
/// Checks if cancelled.
///
/// The cancel token.
private static void CheckIfCancelled(CancellationToken cancellationToken)
{
if (cancellationToken == null)
throw new ArgumentNullException("cancellationToken");
cancellationToken.ThrowIfCancellationRequested();
}
}
}
Output:
2/18/2013 4:17:13 PM
2/18/2013 4:17:15 PM
2/18/2013 4:17:17 PM
2/18/2013 4:17:19 PM
2/18/2013 4:17:21 PM
2/18/2013 4:17:23 PM
2/18/2013 4:17:25 PM
2/18/2013 4:17:27 PM
2/18/2013 4:17:29 PM
2/18/2013 4:17:31 PM
Finished!
Press any key to continue . . .