C# · 12月 27, 2021

C#等待Task.Delay(1000);只需640ms才能返回

所以,这是很简单的解释,但我已经搜索,找不到有同样问题的人.我的问题起源于一个长期的定期任务,比我想要的更频繁.似乎每个Task.Delay()我创建并等待返回大约65%的延迟我指定的ms.

这个问题归结为以下代码:大概640-660ms的代码返回(根据visual studio,我在这行代码中设置了一个断点,并且这个代码已经过了多久)

await Task.Delay(1000);

在另外两台机器上,IDENTICAL代码库运行正常.不仅仅是上面这个简单的陈述,而且是周期性的任务.有什么地方会影响Task.Delay(int millisecondsDelay)吗?刻度类型,时钟速度,任何东西,系统时钟???我很茫然…

编辑:

在下面的代码段中,EtMilliseconds的大小从130-140ms是相同的.上述预期持续时间的65%. (除了第一次进入while()是不相关的).

Long EtMilliseconds;Stopwatch etWatch = new Stopwatch();etWatch.Restart();while (true){ EtMilliseconds = etWatch.ElapsedMilliseconds; taskDelay = Task.Delay(200); etWatch.Restart(); await taskDelay;}

编辑2:

以下代码会导致EtMilliseconds再次为131ms左右.使用Thread.Sleep似乎没有影响…

public partial class MainWindow : Window{ public MainWindow() { InitializeComponent(); } private void button_Click(object sender,RoutedEventArgs e) { long EtMilliseconds; Stopwatch etWatch = new Stopwatch(); etWatch.Restart(); while (true) { EtMilliseconds = etWatch.ElapsedMilliseconds; label.Content = EtMilliseconds.ToString(); etWatch.Restart(); Thread.Sleep(200); } }}

这个代码段是一样的,但使用Task.Delay(200).这一个正确地更新了GUI标签(Thread.Sleep没有),它是131或140ms.总是…

public partial class MainWindow : Window{ public MainWindow() { InitializeComponent(); } private async void button_Click(object sender,RoutedEventArgs e) { Task taskDelay; long EtMilliseconds; Stopwatch etWatch = new Stopwatch(); etWatch.Restart(); while (true) { EtMilliseconds = etWatch.ElapsedMilliseconds; label.Content = EtMilliseconds.ToString(); taskDelay = Task.Delay(200); etWatch.Restart(); await taskDelay; } }}

编辑3:

使用DispatcherTimer,我仍然从我的Stopwatch.ElapsedMilliseconds大约130ms …但这是一个奇怪的事情.如果我还更新了DateTime.Now()的显示,它们会增加大约200ms(或稍微更多),这是我期望的.什么?

public partial class MainWindow : Window{ public long etMilliseconds; public Stopwatch etWatch; public MainWindow() { InitializeComponent(); this.DataContext = this; } // System.Windows.Threading.DispatcherTimer.Tick handler // // Updates the current seconds display and calls // InvalidateRequerySuggested on the CommandManager to force // the Command to raise the CanExecuteChanged event. private void dispatcherTimer_Tick(object sender,EventArgs e) { // Updating the Label which displays the current second t@R_744_2419@CurrTime.Text += DateTime.Now.ToString(“yyyy_MMM_dd-hh:mm:ss.fff_tt”) + “\n”; t@R_744_2419@MilliSecElapsed.Text += etWatch.ElapsedMilliseconds + “\n”; etWatch.Restart(); // Forcing the CommandManager to raise the RequerySuggested event CommandManager.InvalidateRequerySuggested(); } private void button_Click(object sender,RoutedEventArgs e) { etWatch = new Stopwatch(); // DispatcherTimer setup DispatcherTimer dispatcherTimer = new System.Windows.Threading.DispatcherTimer(); dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick); dispatcherTimer.Interval = new TimeSpan(0,200); dispatcherTimer.Start(); etWatch.Restart(); }}解决方法 我根据迄今为止所做的实验,提出了一个猜测答案.

Stopwatch类使用Windows “performance counter”.我经常读到在某些系统上它返回不准确的数据.这似乎发生在较旧的硬件和/或较旧的操作系统版本.

例如,时间可以根据您正在执行的核心跳转.这可能不是这里的问题,因为你的时间一直在关闭.但这是这种时间数据的问题的一个例子.

我想Visual Studio也使用秒表.

这也符合这个事实,即问题只发生在您的机器上.其他机器可能有不同的时间硬件.

尝试这个:

var sw = Stopwatch.StartNew();var startDateTime = DateTime.UtcNow;Thread.Sleep(200);sw.Stop();var endDateTime = DateTime.UtcNow;

并发布结果.我的预测是,Stopwatch版本是错误的,基于DateTime的版本显示了超过200ms.

据我所知,Windows内核使用DateTime.UtcNow用于自己的计时器和延迟的时间源. AFAIK是一个硬件中断,默认情况下以60Hz为单位,并使定时器以该速率更新全局时间变量.这意味着即使DateTime.UtcNow是错误的,它应该与Thread.Sleep一致.但是我们知道DateTime.UtcNow是对的.否则你会注意到系统时间漂移很大.

也许您可以尝试禁用使用高频计数器提供Windows的硬件. Stopwatch.IsHighResolution应该返回true,当你禁用这块硬件时应该变为false.在我的机器上,它在设备管理器中被称为“HPET”.