Windows runtime 任务的准确性。延迟

Windows runtime 任务的准确性。延迟,windows-runtime,task-parallel-library,win-universal-app,Windows Runtime,Task Parallel Library,Win Universal App,我正在用C#/Xaml开发windows10universal应用程序,我正在使用waitTask.Delay(delayinmillists)在给定的时间内挂起一个方法。我的场景有点实时,所以它对时间变化非常敏感,我需要确保当我将一个方法挂起8毫秒时,它会挂起8毫秒。我注意到**Task.Delay**挂起方法的实际时间跨度与作为延迟参数传递的时间跨度不同,从1到30毫秒不等,每次调用的“偏差”长度不同。所以,当我想睡眠8毫秒时,我的系统睡眠9到39毫秒,这完全破坏了我的场景。 那么,我的问题

我正在用C#/Xaml开发windows10universal应用程序,我正在使用waitTask.Delay(delayinmillists)在给定的时间内挂起一个方法。我的场景有点实时,所以它对时间变化非常敏感,我需要确保当我将一个方法挂起8毫秒时,它会挂起8毫秒。我注意到**Task.Delay**挂起方法的实际时间跨度与作为延迟参数传递的时间跨度不同,从1到30毫秒不等,每次调用的“偏差”长度不同。所以,当我想睡眠8毫秒时,我的系统睡眠9到39毫秒,这完全破坏了我的场景。 那么,我的问题是,替代**任务。延迟**并达到良好精度的最佳方法是什么?目前,我使用这种方法:

 public static void Delay1(int delay)
    {
        long mt = delay * TimeSpan.TicksPerMillisecond;
        Stopwatch s = Stopwatch.StarNew();
        while (true)
        {
            if (s.Elapsed.TotalMilliseconds > delay)
            {
                return;
            }
        }
    }
但它会消耗大量资源,这是处理器核心的100%。如果用户拥有少量处理器内核,这将是非常不够的。

根据系统时钟分辨率,不可能实现更高的精度:

此方法取决于系统时钟。这意味着 延迟大约等于系统时钟的分辨率,如果 毫秒延迟参数小于 系统时钟,在Windows上约为15毫秒 系统


你应该使用多媒体定时器。这些都非常准确。 请看这里:

我似乎找到了一个sattisfactory解决方案:

新的System.Threading.ManualResetEvent(false).WaitOne(DelayIn毫秒)代替了Wait Task.Delay(DelayIn毫秒)

我使用了以下代码来测试这两种方法:

async void Probe()
    {
        for (int i = 0; i < 1000; i++)
        {
            // METHOD 1
            await Task.Delay(3); 
           // METHOD 2
           new System.Threading.ManualResetEvent(false).WaitOne(3); 
         }
    }
async void Probe()
{
对于(int i=0;i<1000;i++)
{
//方法1
等待任务。延迟(3);
//方法2
新系统。线程。手动重置事件(错误)。WaitOne(3);
}
}

上面的代码应该正好需要3秒钟才能执行。对于方法2,大约需要3300毫秒,因此每次调用的eror为0.3毫秒。我可以接受。但是方法1需要大约15秒(!)才能执行,这对于我的场景来说是完全不可接受的错误。我只想知道方法2的CPU有什么用途,希望它不使用轮询,文档中提到了信号的使用,但不幸的是,它并不自动意味着轮询不在其他地方使用。

经过努力,我找到了一个解决方案,可以让线程休眠特定时间,而在我的Core 2 Duo CPU 3.00 GHz上,错误仅为3到4微秒。 这是:

下面是代码。(结尾也给出了C代码。)使用“ThreadSleepHandler”:

对于c#程序员,这里有一些代码(我已经转换了这些代码,但我不确定):

静态类Main
{
公共静态void Main()
{
ThreadSleepHandler=新的ThreadSleepHandler();
秒表秒表=新秒表();
做{
stopwatch.Restart();
Sleep(TimeSpan.FromSeconds(1));
秒表;
控制台写入线(秒表已过);
}虽然(正确);
}
}
公共类ThreadSleepHandler
{
私人秒表秒表=新秒表();
私人秒表旋转秒表=新秒表();
专用时间跨度平均值=新时间跨度();
private Threading.SpinWait微调器;
公共无效睡眠(TimeSpan时间)
{
Stopwatch.Restart();
if(Average.Ticks==0)
平均值=GetSpinTime();
while(秒表时间
注: “ThreadSleepHandler”是线程不安全的。不能对多个线程使用单个“ThreadSleepHandler”


第一次睡眠时间不够精确。

在我的测试中,我发现如果必须用代码来完成,每隔20毫秒的Dispatchermer将提供足够平滑的动画。如前所述,在XAML中执行此操作是另一种选择。

Task.Delay不是计时器,也不用于精确计时。这不仅是因为它,还因为它在线程池线程上调度其延续而产生了成本。操作系统是这样的,所以你要求一个超出限制的延迟方式。你想做什么?你为什么想要这么高的精确度?可能还有其他方法可以实现同样的目标。我想不出任何通用应用程序需要内核级精度的情况。你是想控制动画还是声音播放?@Panagiotis Kanavos:是的,准确地说,我是在控制动画、声音播放和网络使用,这就是为什么我需要这样的准确性。你不需要这样的准确性,你只需要在XAML中设置正确的转换计时。与其试图一帧一帧地控制事情,不如简单地。NET将确保正确的动画从正确的点开始执行。请阅读底部,以查看xcoder37最终提出的惊人解决方案:一个不消耗100%cpu时间的计时器,在任何时间跨度内精确到半毫秒以下。当我需要更高的精确度时,我会在大部分定时等待中使用它,然后旋转最后一毫秒左右,检查秒表。完美的这不会有帮助,因为Task.Delay已经使用了这些计时器。建议用于多媒体计时器的方法已过时,并被CreateTimerQueueTimer取代。Task.Delay在内部使用System.Threading.Timer,它。。。使用CreateTimerQueueTimer。查看我的答案。误差只有3到4微秒。这里:你会推荐任何高分辨率的精确替代方法吗?例如System.Diagnostics.Stopwatch基于**QueryPerformanceCounter**API(),但到目前为止,我提出的唯一解决方案是将秒表置于循环中,这确实是准确的,但占用了大量资源
Public Class ThreadSleepHandler
Private Stopwatch As New Stopwatch
Private SpinStopwatch As New Stopwatch
Private Average As New TimeSpan
Private Spinner As Threading.SpinWait
Public Sub Sleep(Time As TimeSpan)
    Stopwatch.Restart()
    If Average.Ticks = 0 Then Average = GetSpinTime()
    While Stopwatch.Elapsed < Time
        If Stopwatch.Elapsed + Average < Time Then
            Average = TimeSpan.FromTicks((Average + GetSpinTime()).Ticks / 2)
        End If
    End While
End Sub
Public Function GetSpinTime() As TimeSpan
    SpinStopwatch.Restart()
    Spinner.SpinOnce()
    SpinStopwatch.Stop()
    Return SpinStopwatch.Elapsed
End Function
End Class
Sub Main()
    Dim handler As New ThreadSleepHandler
    Dim stopwatch As New Stopwatch
    Do
        stopwatch.Restart()
        handler.Sleep(TimeSpan.FromSeconds(1))
        stopwatch.Stop()
        Console.WriteLine(stopwatch.Elapsed)
    Loop
End Sub
static class Main
{
public static void Main()
{
    ThreadSleepHandler handler = new ThreadSleepHandler();
    Stopwatch stopwatch = new Stopwatch();
    do {
        stopwatch.Restart();
        handler.Sleep(TimeSpan.FromSeconds(1));
        stopwatch.Stop();
        Console.WriteLine(stopwatch.Elapsed);
    } while (true);
}
}
public class ThreadSleepHandler
{
private Stopwatch Stopwatch = new Stopwatch();
private Stopwatch SpinStopwatch = new Stopwatch();
private TimeSpan Average = new TimeSpan();
private Threading.SpinWait Spinner;
public void Sleep(TimeSpan Time)
{
    Stopwatch.Restart();
    if (Average.Ticks == 0)
        Average = GetSpinTime();
    while (Stopwatch.Elapsed < Time) {
        if (Stopwatch.Elapsed + Average < Time) {
            Average = TimeSpan.FromTicks((Average + GetSpinTime()).Ticks / 2);
        }
    }
}
public TimeSpan GetSpinTime()
{
    SpinStopwatch.Restart();
    Spinner.SpinOnce();
    SpinStopwatch.Stop();
    return SpinStopwatch.Elapsed;
}
}