Task Delay vs Thread Sleep Resolution Accuracy

2019-07-13 08:57发布

问题:

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

回答1:

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.



回答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