3

i need to execute a function after a specific amount of time, therefore i use this code.

_start = DateTime.Now;
await Task.Delay(_app.Settings.AudioFileStartVarianz.BaseSpan).ContinueWith(x => 
        {
            Console.WriteLine(DateTime.Now - _start);

        });

Lets say i want to wait for exactly 0.04 seconds. My problem now is that it's not working precise enough. I get following output after calling the function 5 times:

  • 00:00:00.0414220
  • 00:00:00.0536098
  • 00:00:00.0507841
  • 00:00:00.0467757
  • 00:00:00.0425790

if i use this code it works way better

        _start = DateTime.Now;
        Thread.Sleep(_app.Settings.AudioFileStartVarianz.BaseSpan);
        Console.WriteLine(DateTime.Now - _start);
  • 00:00:00.0405879
  • 00:00:00.0404284
  • 00:00:00.0402117
  • 00:00:00.0404908
  • 00:00:00.0409088

But now i have the problem, that the function is not running asynchronous, what is bad because i am playing an audio file (NAudio).

Any ideas how i can here wait async, so that my audio file is not stopping?

KR Manuel

Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321

2 Answers2

5

The difference between the two calls, Thread.Sleep & Task.Delay, is that Thread.Sleep calls an OS method to sleep the thread and Task.Delay creates a Timer to simulate the delay.

The calls are effectively...

private static extern int WaitOneNative(SafeHandle waitableSafeHandle, uint millisecondsTimeout, bool hasThreadAffinity, bool exitContext);

...and...

promise.Timer = new Timer(state => ((DelayPromise)state).Complete(), promise, millisecondsDelay, Timeout.Infinite);

...respectively.

Now timers in windows are notoriously low resolution - with a 15ms error or so. I think it's because of this that you're getting your results.

The bad news is that sleeping the thread is not asynchronous, as you stated, so to get asynchrony you need to accept the timer resolution error. I don't think that there is any sane way for you to avoid it.

Enigmativity
  • 113,464
  • 11
  • 89
  • 172
  • It's not the Windows timer that's limited to 15 ms or so resolution. Rather, the .NET wrapper around the Windows timer imposes that limitation. – Jim Mischel Jun 22 '16 at 18:33
  • @JimMischel - My understanding is that the limitation is core the OS (set at 1/64 of a second or 15.625 seconds), but can be changed. Take a read of https://randomascii.wordpress.com/2013/07/08/windows-timer-resolution-megawatts-wasted/. The problem with changing it is power consumption and thus battery life on portable devices. – Enigmativity Jun 22 '16 at 23:27
  • Yes, I've read that. But at least on every desktop and laptop I've worked with, if I create a Timer Queue Timer with a period of 1 ms, I get 1 ms. – Jim Mischel Jun 23 '16 at 01:51
2

To get an accurate timer you should use a multimedia timer :

Multimedia timer services allow applications to schedule timer events with the greatest resolution (or accuracy) possible for the hardware platform. These multimedia timer services allow you to schedule timer events at a higher resolution than other timer services.

You can find an implementation there :

http://www.codeproject.com/Articles/5501/The-Multimedia-Timer-for-the-NET-Framework

So for your example it can be something like

EDIT code simplified :

    Multimedia.Timer mmTimer = new Multimedia.Timer(new Container())
    {
        Mode = Multimedia.TimerMode.OneShot,
        Period = 40,
        Resolution = 1,
        SynchronizingObject = this
    };
    mmTimer.Tick += new System.EventHandler(this.mmTimer_Tick);
    startTime = DateTime.Now;
    mmTimer.Start();

    private void mmTimer_Tick(object sender, System.EventArgs e)
    {           
        MessageBox.Show("ticks after 40ms");
    }

Dont try to check if the timer ticks correctly with DateTime.Now the precision of this is around 15 or 16 ms depending on the system.

Further reading : http://www.nullskull.com/articles/20021111.asp

Quentin Roger
  • 6,410
  • 2
  • 23
  • 36
  • 2
    The multimedia timers have been deprecated for a decade. *Don't use them.* Timer Queue Timers are more flexible, use fewer system resources, and provide the same accuracy as the Multimedia timers. See https://www.informit.com/guides/content.aspx?g=dotnet&seqNum=817 for an implementation and examples. Or, use threadpool timers. But nix the multimedia timer, please. – Jim Mischel Jun 22 '16 at 18:35