Is There a Task Based Replacement for System.Threading.Timer

Is there a Task based replacement for System.Threading.Timer?

It depends on 4.5, but this works.

public class PeriodicTask
{
public static async Task Run(Action action, TimeSpan period, CancellationToken cancellationToken)
{
while(!cancellationToken.IsCancellationRequested)
{
await Task.Delay(period, cancellationToken);

if (!cancellationToken.IsCancellationRequested)
action();
}
}

public static Task Run(Action action, TimeSpan period)
{
return Run(action, period, CancellationToken.None);
}
}

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.

C# - Alternative to System.Timers.Timer, to call a function at a specific time

One approach to task scheduling, simliar to that proposed by klausbyskov, is to built your scheduling service on top of an existing .NET scheduling framework/library. Compared to using the Windows Task Scheduler, this has the advantages of (a) allowing several jobs to be defined in the same project and (b) keeping jobs and scheduling logic "together" - i.e. not relying on server settings prone to get lost in system upgrades/replacements.

I know of two open-source projects that offer this kind of functionality:

  • "Quartz.NET is a full-featured, open source job scheduling system that can be used from smallest apps to large scale enterprise systems." I have never actually used this framework myself, but from studying the website, I have the impression of a very solid tool, providing many cool features. The fact that there [quartz-net] tag on Stackoverflow might also indicate that it is actually used in the wild.

  • "NCron is a light-weight library for building and deploying scheduled background jobs on the .NET server platform." It does not have half as many features as Quartz.NET, and it does not have any tag on Stackoverflow, but the author (yours truly) believes that its low-friction API makes it somewhat easier to get started with.

Building your scheduling service on top of NCron, you can schedule a CleanupJob for weekly execution using a single line of code:

service.Weekly().Run<CleanupJob>();

Ok, you will need around three lines of boiler plate code on top of that to actually turn your project into a Windows service, but it sounds more impressive when I claim that it can be done with one line of code ;)

Alternative to Timer

You might want to look into the System.Threading.Timer instead:
Timer Class

System.Threading.Timer, which executes a single callback method on a
thread pool thread at regular intervals. The callback method is
defined when the timer is instantiated and cannot be changed. Like the
System.Timers.Timer class, this class is intended for use as a
server-based or service component in a multithreaded environment; it
has no user interface and is not visible at runtime.

This link has a nice table of comparison between the different timer classes at the bottom:

Comparing the Timer Classes in the .NET Framework Class Library

Replacing Threading.Timer with a custom async timer class?

Timer ticks are queued to the thread-pool. You can use async and await there without problems. Make your tick function call an async Task function and throw away the resulting task. And document, why.

void TimerProc(...) {
var task = TimerImpl();
//throw away task
}

async Task TimerImpl() {
try { MyCode(); }
catch (Exception ex) { Log(ex); }
}

Note, that timer ticks can be delayed arbitrarily, they can execute concurrently and they can run after you have stopped the timer.

I don't see why you'd need to use async code anyway. You are already on the thread-pool so you don't have to unblock the UI. Writing to local disks does not benefit much from async IO. Maybe the answer is: Just use synchronous code here.

In order to guard against overwriting the CancellationToken you need to copy it to all places that uses it. Nothing should directly use the field.

Better yet, create a new instance of AsyncTimer instead of making it restartable. Avoid state mutation.

Reliable alternative to Timer in .NET framework?

This article describes the difference between the timer classes in the .Net framework.

But maybe another approach can help:

If have have to wait for such a long time it would be better to calculate the DateTime when you like something to start. Afterwards your task wakes up every second (or whatever accuracy is needed) and compares the current time with the desired time. If the current time is equal or greater just start your job. Otherwise go to sleep (till the next second, hour, millisecond, whatever). By the way, that's the way how the Microsoft Task Scheduler works.

Is System.Threading.Timer efficient enough for thousands of concurrent timers?

I will direct you to this post from Raymond Chen:

What is the maximum number of timers a program can create?

Technically there is no problem with creating thousands of them. Just be aware that these use global system resources, so you will eventually hit an upper limit and/or start starving other programs (including Windows itself) if you go crazy with it.

Also make sure you dispose the timers when you're done with them, otherwise you'll end up with a huge resource leak.


I do have to say, this sounds like something you could implement with a single timer; just maintain a dictionary of active requests and timeouts, and on each tick, go through the whole dictionary and check each entry. You could improve performance by making it a sorted list; that way if nothing is timing out for another 5 minutes, the tick method will exit after looking at the first entry.

In addition to the above, you could also dynamically adjust the tick interval to fire whenever the next request is scheduled to time out; then on each tick, reschedule the next tick to the next timeout. This would take up virtually no system resources (just one timer) and also be very fast (as with your one-timer-per-request idea, you would only be running expensive code when requests actually expire).

Even though you can create thousands of timers, the above approach will scale far better and will also be a lot easier to test and maintain.

Proper way to implement a never ending task. (Timers vs Task)

I'd use TPL Dataflow for this (since you're using .NET 4.5 and it uses Task internally). You can easily create an ActionBlock<TInput> which posts items to itself after it's processed it's action and waited an appropriate amount of time.

First, create a factory that will create your never-ending task:

ITargetBlock<DateTimeOffset> CreateNeverEndingTask(
Action<DateTimeOffset> action, CancellationToken cancellationToken)
{
// Validate parameters.
if (action == null) throw new ArgumentNullException("action");

// Declare the block variable, it needs to be captured.
ActionBlock<DateTimeOffset> block = null;

// Create the block, it will call itself, so
// you need to separate the declaration and
// the assignment.
// Async so you can wait easily when the
// delay comes.
block = new ActionBlock<DateTimeOffset>(async now => {
// Perform the action.
action(now);

// Wait.
await Task.Delay(TimeSpan.FromSeconds(10), cancellationToken).
// Doing this here because synchronization context more than
// likely *doesn't* need to be captured for the continuation
// here. As a matter of fact, that would be downright
// dangerous.
ConfigureAwait(false);

// Post the action back to the block.
block.Post(DateTimeOffset.Now);
}, new ExecutionDataflowBlockOptions {
CancellationToken = cancellationToken
});

// Return the block.
return block;
}

I've chosen the ActionBlock<TInput> to take a DateTimeOffset structure; you have to pass a type parameter, and it might as well pass some useful state (you can change the nature of the state, if you want).

Also, note that the ActionBlock<TInput> by default processes only one item at a time, so you're guaranteed that only one action will be processed (meaning, you won't have to deal with reentrancy when it calls the Post extension method back on itself).

I've also passed the CancellationToken structure to both the constructor of the ActionBlock<TInput> and to the Task.Delay method call; if the process is cancelled, the cancellation will take place at the first possible opportunity.

From there, it's an easy refactoring of your code to store the ITargetBlock<DateTimeoffset> interface implemented by ActionBlock<TInput> (this is the higher-level abstraction representing blocks that are consumers, and you want to be able to trigger the consumption through a call to the Post extension method):

CancellationTokenSource wtoken;
ActionBlock<DateTimeOffset> task;

Your StartWork method:

void StartWork()
{
// Create the token source.
wtoken = new CancellationTokenSource();

// Set the task.
task = CreateNeverEndingTask(now => DoWork(), wtoken.Token);

// Start the task. Post the time.
task.Post(DateTimeOffset.Now);
}

And then your StopWork method:

void StopWork()
{
// CancellationTokenSource implements IDisposable.
using (wtoken)
{
// Cancel. This will cancel the task.
wtoken.Cancel();
}

// Set everything to null, since the references
// are on the class level and keeping them around
// is holding onto invalid state.
wtoken = null;
task = null;
}

Why would you want to use TPL Dataflow here? A few reasons:

Separation of concerns

The CreateNeverEndingTask method is now a factory that creates your "service" so to speak. You control when it starts and stops, and it's completely self-contained. You don't have to interweave state control of the timer with other aspects of your code. You simply create the block, start it, and stop it when you're done.

More efficient use of threads/tasks/resources

The default scheduler for the blocks in TPL data flow is the same for a Task, which is the thread pool. By using the ActionBlock<TInput> to process your action, as well as a call to Task.Delay, you're yielding control of the thread that you were using when you're not actually doing anything. Granted, this actually leads to some overhead when you spawn up the new Task that will process the continuation, but that should be small, considering you aren't processing this in a tight loop (you're waiting ten seconds between invocations).

If the DoWork function actually can be made awaitable (namely, in that it returns a Task), then you can (possibly) optimize this even more by tweaking the factory method above to take a Func<DateTimeOffset, CancellationToken, Task> instead of an Action<DateTimeOffset>, like so:

ITargetBlock<DateTimeOffset> CreateNeverEndingTask(
Func<DateTimeOffset, CancellationToken, Task> action,
CancellationToken cancellationToken)
{
// Validate parameters.
if (action == null) throw new ArgumentNullException("action");

// Declare the block variable, it needs to be captured.
ActionBlock<DateTimeOffset> block = null;

// Create the block, it will call itself, so
// you need to separate the declaration and
// the assignment.
// Async so you can wait easily when the
// delay comes.
block = new ActionBlock<DateTimeOffset>(async now => {
// Perform the action. Wait on the result.
await action(now, cancellationToken).
// Doing this here because synchronization context more than
// likely *doesn't* need to be captured for the continuation
// here. As a matter of fact, that would be downright
// dangerous.
ConfigureAwait(false);

// Wait.
await Task.Delay(TimeSpan.FromSeconds(10), cancellationToken).
// Same as above.
ConfigureAwait(false);

// Post the action back to the block.
block.Post(DateTimeOffset.Now);
}, new ExecutionDataflowBlockOptions {
CancellationToken = cancellationToken
});

// Return the block.
return block;
}

Of course, it would be good practice to weave the CancellationToken through to your method (if it accepts one), which is done here.

That means you would then have a DoWorkAsync method with the following signature:

Task DoWorkAsync(CancellationToken cancellationToken);

You'd have to change (only slightly, and you're not bleeding out separation of concerns here) the StartWork method to account for the new signature passed to the CreateNeverEndingTask method, like so:

void StartWork()
{
// Create the token source.
wtoken = new CancellationTokenSource();

// Set the task.
task = CreateNeverEndingTask((now, ct) => DoWorkAsync(ct), wtoken.Token);

// Start the task. Post the time.
task.Post(DateTimeOffset.Now, wtoken.Token);
}


Related Topics



Leave a reply



Submit