Tag Archives: Timer

A slightly more accurate Task.Delay (.NET/C#)

The timer used in Task.Delay is pretty inaccurate, around 15 [ms]. This gave me some headache trying to throttle a stress client firing async write commands against a Redis database. Here a suggestion to improve accuracy close to 1 [ms] by using the multimedia timers from Microsoft. The overhead caused by TPL cannot be avoided.

Timer.cs by Leslie Sanford nicely wraps the Multimedia timer. Download source from https://www.codeproject.com/Articles/5501/The-Multimedia-Timer-for-the-NET-Framework.

TaskHelper.Delay

using System;
using System.Threading.Tasks;
using Multimedia;
 
namespace Demo
{
    public class TaskHelper
    {
        private static readonly ConcurrentDictionary<TaskCompletionSource<bool>, Timer> s_timers = new ConcurrentDictionary<TaskCompletionSource<bool>, Timer>();
 
        public static Task<bool> Delay(int millisecondsDelay)
        {
            var tcs = new TaskCompletionSource<bool>();
 
            if (millisecondsDelay <= 0)
            {
                tcs.SetResult(true);
                return tcs.Task;
            }
 
            var timer = new Timer();
            timer.Mode = TimerMode.OneShot;
            timer.Period = millisecondsDelay;
 
            s_timers.TryAdd(tcs, timer);
 
            timer.Tick += (object sender, EventArgs e) => {
                tcs.SetResult(true);
                Timer empty;
                s_timers.TryRemove(tcs, out empty);
            };
 
            timer.Start();
 
            return tcs.Task;
        }
    }
}

Sample usage

using System;
using System.Threading.Tasks;
 
namespace Demo
{
    class Program
    {
        static void Main(string[] args)
        {
            Task.Run(
                async() => 
                {
                    var stopWatch = System.Diagnostics.Stopwatch.StartNew();
 
                    for (int i = 0; i < 1000; i++) {
                         //await Task.Delay(1).ConfigureAwait(false);
                         await TaskHelper.Delay(1).ConfigureAwait(false);
                    }
 
                    stopWatch.Stop();
 
                    Console.WriteLine($"Time elpased {stopWatch.ElapsedMilliseconds} [ms]");
                }
            ).Wait();
 
            Console.WriteLine("Press enter to exit");
            Console.ReadKey();
        }
    }
}

Result

Time elpased 1701 [ms]
Press enter to exit

As expected, there is some overhead by the TPL. In comparison, using the await Task.Delay(1); takes up to 15623 [ms].

References
http://stackoverflow.com/questions/31742521/accuracy-of-task-delay