Tag Archives: .NET

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

Minimal sample: Self hosted Nancy using Owin, Unity bootstrapper including xUnit test

Note: Depending on your environment, you might need to make a name space reservation

netsh http add urlacl url=http://+:8080/ user=DOMAIN\username

See also Hosting Nancy with Owin for more details.

Programm.cs

using Microsoft.Owin.Hosting;
 
namespace NancyOwinBox
{
    class Program
    {
        static void Main(string[] args)
        {
            var url = "http://+:8080";
 
            using (WebApp.Start(url))
            {
                Console.WriteLine("Running on {0}", url);
                Console.WriteLine("Press enter to exit");
                Console.ReadLine();
            }
        }
    }
}

HomeModule.cs

using Nancy.Owin;
 
namespace NancyOwinBox
{
    public class HomeModule : NancyModule
    {
        public HomeModule(IMessageHelper messageHelper)
        {
            Get["/"] = x =>
            {
                var env = this.Context.GetOwinEnvironment();
                return string.Format(messageHelper.HelloWorld());
            };
        }
    }
}

Startup.cs

using Microsoft.Practices.Unity;
using Nancy.Owin;
using Owin;
 
namespace NancyOwinBox
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            IUnityContainer container = new UnityContainer();
 
            container.RegisterType<IMessageHelper, MessageHelper>();
 
            app.UseNancy(new NancyOptions
            {
                EnableClientCertificates = true,
                Bootstrapper = new NancyOwinBoxBootstrapper(container)
            });
        }
    }
}

NancyOwinBoxBootrapper.cs

using Microsoft.Practices.Unity;
using Nancy.Bootstrappers.Unity;
 
namespace NancyOwinBox
{
    public class NancyOwinBoxBootstrapper : UnityNancyBootstrapper
    {
        private IUnityContainer _container;
 
        public NancyOwinBoxBootstrapper(IUnityContainer container)
        {
            _container = container;
        }
 
        protected override IUnityContainer GetApplicationContainer()
        {
            return _container;
        }
    }
}

xUnit 2.x test file: NancyOwinBoxTests.cs

using Microsoft.Practices.Unity;
using Nancy;
using Nancy.Testing;
using Xunit;
 
namespace NancyOwinBox.Testing
{
    public class NancyOwinBoxTests
    {
        [Fact]
        public void ShouldGetHomeScreen() {
 
            IUnityContainer container = new UnityContainer();
 
            container.RegisterType<IMessageHelper, MessageHelper>();
 
            NancyOwinBoxBootstrapper bootstrapper = new NancyOwinBoxBootstrapper(container);
 
            var browser = new Browser(bootstrapper);
 
            var result = browser.Get("/", with =>
            {
                with.HttpRequest();
            });
 
            Assert.Equal(HttpStatusCode.OK, result.StatusCode);
 
            Assert.True(true);
        }
    }
}

package.config – NancyOwinBox

<packages>
  <package id="CommonServiceLocator" version="1.0" targetFramework="net45" >
  <package id="Microsoft.Owin" version="3.0.1" targetFramework="net45" >
  <package id="Microsoft.Owin.Host.HttpListener" version="3.0.1" targetFramework="net45" >
  <package id="Microsoft.Owin.Hosting" version="3.0.1" targetFramework="net45" >
  <package id="Nancy" version="1.1" targetFramework="net45" /&gt;
  <package id="Nancy.Bootstrappers.Unity" version="1.1" targetFramework="net45" >
  <package id="Nancy.Owin" version="1.1" targetFramework="net45" >
  <package id="Owin" version="1.0" targetFramework="net45" >
  <package id="Unity" version="2.1.505.0" targetFramework="net45" >
</packages>

package.config. NancyOwinBox.Testing

<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="CsQuery" version="1.3.3" targetFramework="net45" />
  <package id="Nancy" version="1.1" targetFramework="net45" />
  <package id="Nancy.Bootstrappers.Unity" version="1.1" targetFramework="net45" />
  <package id="Nancy.Testing" version="1.1" targetFramework="net45" />
  <package id="Unity" version="3.5.1404.0" targetFramework="net45" />
  <package id="xunit" version="2.0.0" targetFramework="net45" />
  <package id="xunit.abstractions" version="2.0.0" targetFramework="net45" />
  <package id="xunit.assert" version="2.0.0" targetFramework="net45" />
  <package id="xunit.core" version="2.0.0" targetFramework="net45" />
  <package id="xunit.extensibility.core" version="2.0.0" targetFramework="net45" />
  <package id="xunit.runner.visualstudio" version="2.0.0" targetFramework="net45" />
</packages>

Download sample solution including all sources:
NancyOwinBox.zip – Visual Studio 2013