我正在使用C#/ Xaml开发Windows 10 Universal App,我正在使用等待 Task.Delay(delayInMilliseconds)来暂停给定时间的方法。我的场景有点实时,所以它对时间变化非常敏感,我需要确保当我暂停一个方法让我们说8毫秒时,它被暂停8个小时。我注意到** Task.Delay **暂停方法的实际时间跨度与传递的延迟参数不同,1到30毫秒,每次调用“偏差”的长度不同。所以,当我想睡8毫秒时,我的系统会睡眠9到39毫秒,这完全破坏了我的情景。 所以,我的问题是什么是替换** Task.Delay **并获得良好精度的最佳方法?目前我使用这种方法:
public static void Delay1(int delay)
{
long mt = delay * TimeSpan.TicksPerMillisecond;
Stopwatch s = Stopwatch.StarNew();
while (true)
{
if (s.Elapsed.TotalMilliseconds > delay)
{
return;
}
}
}
但它猜测它消耗了大量资源,即处理器核心的100%。如果用户的处理器核心数量很少,那将非常不足。
答案 0 :(得分:5)
根据msdn,由于系统时钟分辨率,无法实现更高的准确度:
此方法取决于系统时钟。这意味着时间 延迟将近似等于系统时钟的分辨率 millisecondsDelay参数小于的分辨率 系统时钟,在Windows上约为15毫秒 系统
答案 1 :(得分:1)
您应该使用多媒体计时器。这些都很准确。 看看这里: https://social.msdn.microsoft.com/Forums/vstudio/en-US/2024e360-d45f-42a1-b818-da40f7d4264c/accurate-timer
答案 2 :(得分:1)
好像我找到了一个令人满意的解决方案:
new System.Threading.ManualResetEvent(false).WaitOne(delayInMilliseconds)而不是 await Task.Delay(delayInMilliseconds);
我使用以下代码来测试这两种方法:
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);
}
}
上面的代码应该花费3秒钟才能执行。使用METHOD 2需要大约3300 ms,因此每次调用的误差为0.3 ms。我能接受。但是方法1执行大约15秒(!)来执行,这给我的场景带来了完全不可接受的错误。我只想知道使用方法2的CPU的用法是什么,希望它不使用轮询,文档说明了信号的使用,但不幸的是它并不自动意味着轮询不会在其他地方使用。
答案 3 :(得分:0)
努力工作后,我找到了一个让线程在特定时间内休眠的解决方案,我的Core 2 Duo CPU 3.00 GHz上的误差仅为3到4微秒。 这是:
这是代码。(C#代码也在最后给出。)使用&#34; ThreadSleepHandler&#34;:
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
对于c#程序员来说这里是代码(我已经转换了这段代码,但我不确定):
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;
}
}
注意: &#34; ThreadSleepHandler&#34;线程不安全。你不能使用单个&#34; ThreadSleepHandler&#34;多线程。
第一次睡眠时间不够准确。
答案 4 :(得分:0)
在我的测试中,我发现如果必须在代码中执行,则20ms间隔的DispatcherTimer将提供足够平滑的动画。如上所述,在XAML中执行此操作是另一种选择。