开发者

More precise Thread.Sleep

开发者 https://www.devze.com 2023-01-18 03:22 出处:网络
How can i 开发者_如何学Godo Thread.Sleep(10.4166667);? OK i see now that Sleep is not the way to go.

How can i 开发者_如何学Godo Thread.Sleep(10.4166667);?

OK i see now that Sleep is not the way to go. So i use Timer but timer is also in ms put i need more precise Is there timer with nanosecond accuracy?


So you want your thread to sleep precisely for that time and then resume? Forget about it. This parameter tells the system to wake the Thread after at least this number of milliseconds. At least. And after resuming, the thread could be put to sleep once again in a blink of an eye. That just how Operating Systems work and you cannot control it.


Please note that Thread.Sleep sleeps as long as you tell it (not even precisely), no matter how long code before or after takes to execute.

Your question seems to imply that you want some code to be executed in certain intervals, since a precise time seems to matter. Thus you might prefer a Timer.


To do such a precise sleep you would need to use a real time operating system and you would likely need specialized hardware. Integrity RTOS claims to respond to interrupts in nanoseconds, as do others.

This isn't going to happen with C# or any kind of high level sleep call.


Please note that the argument is in milliseconds, so 10 is 10 milliseconds. Are you sure you want 10.41 etc milliseconds? If you want 10.41 seconds, then you can use 10416.


The input to Thread.Sleep is the number of milliseconds for which the thread is blocked. After that it will be runnable, but you have no influence over when it is actually scheduled. I.e. in theory the thread could wait forever before resuming execution.

It hardly ever makes sense to rely on specific number of milliseconds here. If you're trying to synchronize work between two threads there are better options than using Sleep.


As you already mentioned: You could combine DispatcherTimer with Stopwatch (Making sure the IsHighResolution and Frequency suits your needs). Start the Timer and the Stopwatch, and on discreet Ticks of the Timer check the exact elapsed time of the stopwatch.


If you are trying to rate-limit a calculation and insist on using only Thread.Sleep then be aware there is a an underlying kernel pulse rate (roughly 15ms), so your thread will only resume when a pulse occurs. The guarantee provided is to "wait at least the specified duration." For example, if you call Thread.Sleep(1) (to wait 1ms), and the last pulse was 13ms ago, then you will end up waiting 2ms until the next pulse comes.

The draw synchronization I implemented for a rendering engine does something similar to dithering to get the quantization to the 15ms intervals to be uniformly distributed around my desired time interval. It is mostly just a matter of subtracting half the pulse interval from the sleep duration, so only half the invocations wait the extra duration to the next 15ms pulse, and half occur early.

public class TimeSynchronizer {
    //see https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-sleep
    public const double THREAD_PULSE_MS = 15.6d;//TODO read exact value for your system

    public readonly TimeSpan Min = TimeSpan.Zero;
    public TimeSynchronizer(TimeSpan? min = null) {
        if (min.HasValue && min.Value.Ticks > 0L) this.Min = min.Value;
    }

    private DateTime _targetTimeUtc = DateTime.UtcNow;//you may wish to defer this initialization so the first Synchronize() call assuredly doesn't wait
    public void Synchronize() {
        if (this.Min.Ticks > 0L) {
            DateTime nowUtc = DateTime.UtcNow;
            TimeSpan waitDuration = this._targetTimeUtc - nowUtc;
            //store the exact desired return time for the next inerval
            if (waitDuration.Ticks > 0L)
                this._targetTimeUtc += this.Min;
            else this._targetTimeUtc = nowUtc + this.Min;//missed it (this does not preserve absolute synchronization and can de-phase from metered interval times)

            if (waitDuration.TotalMilliseconds > THREAD_PULSE_MS/2d)
                Thread.Sleep(waitDuration.Subtract(TimeSpan.FromMilliseconds(THREAD_PULSE_MS/2d)));
        }
    }
}

I do not recommend this solution if your nominal sleep durations are significantly less than the pulse rate, because it will frequently not wait at all in that case.

The following screenshot shows rough percentile bands on how long it truly takes (from buckets of 20 samples each - dark green are the median values), with a (nominal) minimum duration between frames set at 30fps (33.333ms):

More precise Thread.Sleep


I am suspicious that the exact pulse duration is 1 second / 600, since in SQL server a single DateTime tick is exactly 1/300th of a second

0

精彩评论

暂无评论...
验证码 换一张
取 消