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
        tBoxCurrTime.Text += DateTime.Now.ToString("yyyy_MMM_dd-hh:mm:ss.fff_tt") + "\n";
        tBoxMilliSecElapsed.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”.

相关文章

在要实现单例模式的类当中添加如下代码:实例化的时候:frmC...
1、如果制作圆角窗体,窗体先继承DOTNETBAR的:public parti...
根据网上资料,自己很粗略的实现了一个winform搜索提示,但是...
近期在做DSOFramer这个控件,打算自己弄一个自定义控件来封装...
今天玩了一把WMI,查询了一下电脑的硬件信息,感觉很多代码都...
最近在研究WinWordControl这个控件,因为上级要求在系统里,...