Timers and the Task Parallel Library TaskCompletionSource

Often an application must perform a concurrent background process after some elapsed interval. Developers typically choose a Timer for such an operation. Since .NET's inception, Timers have been part of the Framework. Strangely though, Timers haven't evolved much.

Timer mechanisms essentially work the same today as the early .NET days. A developer schedules a Timer, includes a delegate the timer executes, and away the Timer goes. Separate objects are needed to store a reference to the Timer and the results of the Timer's execution. Wouldn't it be nice to couple results and Timer control in a single class? Task Parallel Library (TPL) includes a class called TaskCompletionSource that enables this scenario.

Timers Revisited

Timers live throughout the .NET Framework. This article's solutions use the Timer in the System.Threading namespace. The sample code below demonstrates declaring and running a Timer.

            Timer timer = null;

            timer = new Timer(obj =>
                Console.WriteLine("Sample Timer ran");
            , null,new TimeSpan(0,0,1), TimeSpan.FromMilliseconds(Timeout.Infinite));

A Timer is configured to run an Action after an elapsed time period. Timers can be scheduled to run continuously on a scheduled interval or once after an interval expires. System.Threading Timers leverage the .NET Thread Pool.

Like most work items in the Thread Pool, an executing Timer should run a short workload. Difficulties can arise when a Timer on a scheduled interval executes a workload that runs longer than the scheduled interval. Timers are concurrent operations and therefore suffer the same problem as other concurrent code. So, for example, care should be exercised when dealing with shared memory.

Like any other piece of code, Timers can generate Exceptions. A developer may want to cancel a scheduled Timer or even schedule some other operation in response to a completed Timer. TPL Task semantics elegantly surface exceptions and other code execution results. Timers are not Tasks. However, that doesn't mean that Tasks are the only path to Task-like semantics.


TaskCompletionSource controls the result properties of an underlying Task<T> class. Some properties and methods on the TaskCompletionSource appear below.

    public class TaskCompletionSource<TResult>
        public TaskCompletionSource();
        public void SetCanceled();
        public void SetException(Exception exception);
        public void SetResult(TResult result);

        public bool TrySetCanceled();
        public bool TrySetException(Exception exception);
        public bool TrySetResult(TResult result);

Like many TPL classes there are two sets of methods for manipulating the class. The "Set" methods change the underlying Task's state from cancelled, faulted, or completed depending on the method. "Try" operations attempt to transition state and return a Boolean indicating the operation's success. An underlying Task in, for example, a cancelled state attempting transition to some other state will generate an Exception using "Set" operation and return "false" with the Try operations.

With a Task underpinning the TaskCompletionSource; TPL patterns like, for example, Continuations are at a developers fingertips. An introduction to Continuations is beyond the scope of this article, but can be found here: Microsoft .NET Framework 4.0 Task Parallel Library Continuations. Think of a Continuation as a callback or event that executes in response to a completed Task. Continuations can be configured to execute whenever a Task completes or, for example, when a Task generates an Exception. The sample Timer solution leverages Continuations and an Extension Method.

Extension Method Implementation

Extension methods are an easy way to extend a class without resorting to sub classing. As stated earlier, Timer implementation difficulties center on having a single place to control a Timer's execution and query a Timer's execution results. A Timer executes a method so the solution extends the Func<T> delegate. An Extension Method implementation appears below.

    static class TcsExts

{ public static TaskCompletionSource<T> StartNewTimer<T>(this Func<T> func, TimeSpan timeout) { Timer timer = null; TaskCompletionSource<T> tcs = new TaskCompletionSource<T>();   timer = new Timer(obj => { try { //Cancelled could have been called if (!(tcs.Task.IsCompleted)) { var val = func();   tcs.TrySetResult(val); } } catch (Exception ex) { tcs.TrySetException(ex); } } , null, timeout, TimeSpan.FromMilliseconds(Timeout.Infinite));   tcs.Task.ContinueWith(t => { Console.WriteLine("Dispose Continuation ran value is " + t.Status.ToString()); timer.Dispose(); } );   return tcs; } }  

As would be expected the code creates a Timer. A Continuation handles Timer disposal. The Continuation runs whenever the Task underlying a TaskCompletionSource transitions to cancelled, faulted, or completed. Disposal could have been handled inside the Timer's executing code, however, this arrangement would have ruled out cancelling the Timer. The IsCompleted check looks for situations where Dispose could not cancel the Timer in time. One downside to the Continuation is that a Continuation is a scheduled Task and therefore, must wait its turn to execute inside of TPL. The code returns a TaskCompletionSource. Now that the Timer is being controlled with Task-like semantics there are a number of avenues to employing a Timer.


The code below demonstrates executing and then cancelling the Timer.

            var tcsCancelled = new Func<object>(() =>
                Console.WriteLine("Timer Task Cancelled ran.. should not get called");
                return null;
            }).StartNewTimer( timeout);

Since cancelling happens when the Dispose method is called; cancelling does not guarantee that the Timer code will not execute.

The example below demonstrates an Exception handling scenario.

            var tcsException = new Func<object>(() =>
                Console.WriteLine("Timer Task Exception ran");
                throw new Exception("Generated exception");
            { tcsException.Task.Wait(); }
            { Console.WriteLine("tcsException threw an exception"); }

Just like any other TPL Task, the thrown Exception will trickle to code pausing on a Wait statement.

Continuations can be connected to the underlying Task object.

            var tcs = new Func<object>(() =>
                Console.WriteLine("Timer Task ran");
                return null;
            tcs.Task.ContinueWith((t) =>
                Console.WriteLine("This continuation also ran.");

One important point is multiple Continuations can be chained to a Task. Only one Continuation is demonstrated above. Remember though; an underlying Continuation is already assigned in the Extension Method.

Also, as mentioned earlier, Timers are concurrent. However, now that the Timer result is controlled by a concurrent friendly class like TaskCompletionSource a developer can worry a little less.

Finally, code is not limited to a Func<T>. Simply casting to a Method or delegate with the Func<T> signature like in the sample below will allow access to the Extension Method.

            //Can use anything matching the Func signature
            var tcsAlso = ((Func<object>)Program.FuncToRun).StartNewTimer(timeout);


Timers have not evolved much since the early .NET Framework days. Being a form of concurrent code a Timer benefits from new classes in the .NET Framework Task Parallel Library. Wrapping a Timer in a single TaskCompletionSource class serves multiple functions often implemented in two or more classes. Among the functions are Control over the Timer and a concurrent friendly way to view a Timer's status.


"Mechanisms for Creating Tasks"


"Comparing the Timer Classes in the .NET Framework Class Library"

About the Author

Jeffrey Juday

Jeff is a software developer specializing in enterprise application integration solutions utilizing BizTalk, SharePoint, WCF, WF, and SQL Server. Jeff has been developing software with Microsoft tools for more than 15 years in a variety of industries including: military, manufacturing, financial services, management consulting, and computer security. Jeff is a Microsoft BizTalk MVP. Jeff spends his spare time with his wife Sherrill and daughter Alexandra.

Related Articles


  • timer

    Posted by ushacy on 09/05/2011 03:17am

    I want the implementation of timer for windows in c with out using for loops. i tried using different soln but not satisfying my criteria please help me in this concern.

    • RE:timer

      Posted by JeffreyJ on 09/05/2011 08:35am

      I'm not sure how you would do this in C. All .NET code is implemented using the underlying Windows API and therefore Windows API (C/C++). I think there is a managed code (.NET) implementation of C++, but I've never used it. Also, I haven't written C++ code in 12 years. I googled bing with "windows api timer" and found the link below. http://support.microsoft.com/kb/180736 Sincerely, Juday

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Moving from an on-premises environment to Office 365 does not remove the need to plan for disruptions or reduce the business risk requirements for protecting email services. If anything, some risks increase with a move to the cloud. Read how to ease the transition every business faces if considering or already migrating to cloud email. This white paper discusses: Setting expectations when migrating to Office 365 Understanding the implications of relying solely on Exchange Online security Necessary archiving …

  • Enterprises are increasingly looking to platform as a service (PaaS) to lower their costs and speed their time to market for new applications. Developing, deploying, and managing applications in the cloud eliminates the time and expense of managing a physical infrastructure to support them. PaaS offerings must deliver additional long-term benefits, such as a lower total cost of ownership (TCO), rapid scalability, and ease of integration, all while providing robust security and availability. This report …

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date